Commit f8a4c4ae by SHINDAESUB

first commit

parents
TEACHING_RESOLUTION=(3840x2160)
TEACHING_IMAGE_PATH=/home/firefly/2001_0033_neuromorphic/falinux/old_2020/sds/backend/public/image/capture.jpg
\ No newline at end of file
TEACHING_RESOLUTION=(3840x2160)
TEACHING_IMAGE_PATH=/home/firefly/2001_0033_neuromorphic/falinux/old_2020/sds/backend/public/image/capture.jpg
node_modules
public/
# Node.js 설치
## 설치
- curl -sL https://deb.nodesource.com/setup_8.x | sudo -E bash -
- sudo apt-get update
- sudo apt-get install -y nodejs build-essential
## 프로젝트 실행 안될 경우
- 프로젝트 폴더/ rm -rf node_modules
- 프로젝트 폴더/ npm install
## NPM 이상 있을 경우
- sudo npm cache clean --force
- Sudo npm install –g n
- sudo n stable
- sudo npm install –g npm
* 참고 : https://d2fault.github.io/2018/04/30/20180430-install-and-upgrade-nodejs-or-npm/
## 버전확인
- Node : node.js -v
- NPM : npm –v
## Vue 설치
- sudo npm install -g @vue/cli
## cache 삭제
- sudo npm cache verify
- sudo npm cache clean --force
test
\ No newline at end of file
const express = require('express')
const http = require('http')
const path = require('path')
const fs = require('fs');
const dgram = require('dgram');
const dotenv = require('dotenv');
const app = express()
const bodyParser = require('body-parser'); //
dotenv.config({
path: path.resolve(
process.cwd(),
process.env.NODE_ENV == "production" ? ".env" : ".env.dev"
)
});
const server = dgram.createSocket('udp4');
// socket 실행
server.bind(9400);
server.on('listening', function() {
console.log('Socket Port : 9400');
});
server.on('message', function(msg, rinfo) {
udpResultMsg = msg.toString();
});
server.on('close', function() {
console.log('Server UDP Socket close');
});
const client = dgram.createSocket('udp4');
const clientPort= 9300;
// const clientPort= 4403;
// const clientHost = '192.168.52.122'; //inet
const clientHost ='127.0.0.1';
// Error [ERR_SOCKET_DGRAM_NOT_RUNNING]: Not running
client.on('close', function() {
console.log('Client UDP socket close')
});
const jsonFile = fs.readFileSync('./json/projects.json', 'utf8');
// console.log(jsonFile);
const jsonData = JSON.parse(jsonFile);
console.log(jsonData);
//Post 방식은 Get 과 다르기 때문에 body-parser 를 설치해서 사용해야한다.
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: false}))
//vue router 연동하기 위한 설정
app.use(require('connect-history-api-fallback')());
app.use(express.static(path.join(__dirname, 'public')));
app.set('port',process.env.PORT || 3000);
/* udp 통신으로 매니저에게 받는 결과값*/
let udpResultMsg = ''
/* udp 통신 기다리는 시간*/
let udpAwaitTime = 0
let newProjectNum = 0
//json 파일 불러오기
app.get('/api/getProjectList',(req,res) => {
res.send(jsonData)
})
//USER - 검수 완료후 티칭 정보 수정
app.post('/api/teachingInfoModify',(req,res) => {
console.log("수정할 번호 req.body.project_num :"+ req.body.project_num)
fs.readFile('./json/projects.json', 'utf8',(err ,data) => {
let showData = JSON.parse(data)
for(var key in showData){
if(showData[key].project_num === req.body.project_num){
if(req.body.result){
showData[key].current_done++
}else{
showData[key].current_reject++
}
showData[key].current_counter++
}
}
fs.writeFile('./json/projects.json', JSON.stringify(showData), (err)=>{
if (err) throw err;
console.log('JSON File Update Complete');
});
})
}),
//ADMIN - project 새 번호 가져오기 : 캡처 이미지 이름과 프로젝트 생성하기 위함
app.get('/api/projectCreateNum',(req,res) => {
fs.readFile('./json/project_num_seq.json', 'utf8', (err ,data) => {
let showData = JSON.parse(data)
newProjectNum = ++showData[0].project_num_seq
console.log("newProjectNum : ",newProjectNum)
if(err)
console.error("읽어오기 실패")
res.json(newProjectNum)
});
}),
// ADMIN - 프로젝트 생성
app.post('/api/projectCreate',(req,res) => {
console.log(" ------------------------- ")
console.log(" 메시지 뭐냐? ",req.body)
console.log(" ------------------------- ")
fs.readFile('./json/projects.json', 'utf8',(err ,data) => {
let showData = JSON.parse(data)
let retrunMsg = ''
showData[newProjectNum-1] = {
project_num: newProjectNum,
project_name: req.body.project.project_name,
total_num: req.body.project.total_num,
project_user: req.body.project.project_user,
project_admin: req.body.project.project_admin,
current_counter: req.body.project.current_counter,
current_done: req.body.project.current_done,
current_reject: req.body.project.current_reject,
image_path: req.body.project.image_path,
start_date: req.body.project.start_date,
end_date: req.body.project.end_date,
project_state: '',
success_teaching:req.body.successRect,
fail_teaching:req.body.failRect
},
retrunMsg = `${req.body.project_name} 저장 되었습니다.`
console.log('showData :' ,showData)
console.log("newProjectNum :",newProjectNum)
fs.writeFile('./json/projects.json', JSON.stringify(showData), (err)=>{
if (err) throw res.send(err)
console.log('"projects.json" File Update Complete');
// res.status(200)
res.send( {result:true ,project_num:newProjectNum })
});
fs.readFile('./json/project_num_seq.json', 'utf8',(err ,data) => {
let showData = JSON.parse(data)
console.log('RESULT newProjectNum :' ,newProjectNum)
console.log('RESULT showData :' ,showData[0].project_num_seq)
showData[0].project_num_seq = newProjectNum
fs.writeFile('./json/project_num_seq.json', JSON.stringify(showData), (err)=>{
if (err) throw res.send(err)
console.log('"project_num_seq.json" File Update Complete');
});
})
})
})
//ADMIN - project 상태 (삭제 ,수정 )변경
app.post('/api/projectUpdate',(req,res) => {
console.log(" ------------------------- ")
console.log(" projectUpdate : ? ",req.body.msg)
console.log(" ------------------------- ")
fs.readFile('./json/projects.json', 'utf8',(err ,data) => {
let showData = JSON.parse(data)
let retrunMsg = ''
for(var key in showData){
if(showData[key].project_num === req.body.project_num){
if(req.body.msg === "remove"){
showData[key].project_state = "req.body.msg"
retrunMsg = `${req.body.project_name} 삭제 되었습니다.`
}else if(req.body.msg === "update"){
showData[key].project_admin = req.body.project_admin
showData[key].project_user = req.body.project_user
showData[key].total_num = req.body.total_num
showData[key].start_date = req.body.start_date
showData[key].end_date = req.body.end_date
retrunMsg = `${req.body.project_name} 수정 되었습니다.`
}else {
res.send(err)
}
}
}
fs.writeFile('./json/projects.json', JSON.stringify(showData), (err)=>{
if (err) throw res.send(err)
console.log('JSON File Update Complete');
// res.status(200)
res.send(retrunMsg)
});
})
})
const wrapper = asyncFn => { return (async (req, res, next) => { try { return await asyncFn(req, res, next); } catch (error) { return next(error); } }); };
//https://kjwsx23.tistory.com/199 express 와 async ,await 이슈
//express async await wrapper 함수를 써서 감싸거나 라이브러리를 써야 한다.
app.get('/api/requestManager',wrapper(async (req,res)=>{
req.query.image_path = process.env.TEACHING_IMAGE_PATH
if(req.query.cmd === "neuro_check") req.query.teaching = `${process.env.TEACHING_IMAGE_PATH}-test-2-${process.env.TEACHING_RESOLUTION}-${req.query.teaching_info}`
//req.query.project_num
console.log("param :",JSON.stringify(req.query) )
sendToManager (JSON.stringify(req.query))
let requestMsg = ''
switch(req.query.cmd){
case "neuro_start":
requestMsg = 'neuro_start_done'
break
case "neuro_ready":
requestMsg = 'neuro_ready_done'
break
case "neuro_capture":
requestMsg = 'capture_done'
break
case "neuro_check" :
requestMsg = 'neuro_result'
break
default:
res.status(404)
}
await responseManager(requestMsg)
.then(data => {
res.status(200).json(data)
})
.catch(error =>{
res.status(404).send(error);
})
}))
//UDP 메세지 보냄 통신
function sendToManager(msg){
client.send(msg, 0, msg.length, clientPort, clientHost, function(err) {
if ( err ) return ;
console.log("UDP send Msg ")
});
}
// await Promise 응답해야됨
function responseManager (requestMsg) {
return new Promise((resolve, reject) => {
let delay = 100; //ms 단위로 체크
let waitingTime = 0 //대기 시간
let timer = setInterval(() => {
if(requestMsg === JSON.parse(udpResultMsg).cmd ){
console.log(`json 데이터는 ? ${JSON.parse(udpResultMsg).cmd}`)
resolve(udpResultMsg)
clearInterval(timer)
}else if(waitingTime === 5000){
reject('No response from Manager')
clearInterval(timer)
}else{
waitingTime = waitingTime + delay
}
},delay)
});
}
http.createServer(app).listen(app.get('port'),function(){
console.log('WebServer Port: ' +app.get('port'))
})
[{"project_num_seq":4}]
\ No newline at end of file
[{"project_num":1,"project_name":"프로젝트 1","total_num":5,"project_user":"테스트 유저","project_admin":"테스트 관리자","current_counter":7,"current_done":0,"current_reject":7,"image_path":"/image/capture.png","start_date":"2020-10-30","end_date":"2020-10-30","project_state":"","teaching":[{"id":0,"x":336,"y":149,"width":122,"height":80,"stroke":"#4689B3","listening":false,"state":null,"lx":458,"ly":229},{"id":1,"x":540,"y":309,"width":87,"height":143,"stroke":"#B3468B","listening":false,"state":null,"lx":627,"ly":452},{"id":2,"x":246,"y":245,"width":118,"height":164,"stroke":"#46A9B3","listening":false,"state":null,"lx":364,"ly":409},{"id":3,"x":544,"y":57,"width":114,"height":116,"stroke":"#76B346","listening":false,"state":null,"lx":658,"ly":173}]},{"project_num":2,"project_name":"프로젝트 2","total_num":7,"project_user":"테스트 유저","project_admin":"테스트 관리자","current_counter":0,"current_done":0,"current_reject":0,"image_path":"/image/capture.png","start_date":"2020-10-30","end_date":"2020-10-30","project_state":"","teaching":[{"id":0,"x":468,"y":140,"width":133,"height":120,"stroke":"#F90081","listening":false,"state":null,"lx":601,"ly":260},{"id":1,"x":237,"y":268,"width":139,"height":127,"stroke":"#00F9A9","listening":false,"state":null,"lx":376,"ly":395},{"id":2,"x":458,"y":336,"width":116,"height":110,"stroke":"#00A2F9","listening":false,"state":null,"lx":574,"ly":446}]},{"project_num":3,"project_name":"프로젝트 3","total_num":10,"project_user":"테스트 유저","project_admin":"테스트 관리자","current_counter":0,"current_done":0,"current_reject":0,"image_path":"/image/capture.png","start_date":"2020-10-30","end_date":"2020-10-30","project_state":"","teaching":[]},{"project_num":4,"project_name":"테스트","total_num":15,"project_user":"테스트 유저","project_admin":"테스트 관리자","current_counter":1,"current_done":0,"current_reject":1,"image_path":"/image/capture.png","start_date":"2020-10-30","end_date":"2020-10-30","project_state":"","teaching":[{"id":0,"x":530,"y":78,"width":63,"height":80,"stroke":"#F9001F","listening":false,"state":null,"lx":593,"ly":158},{"id":1,"x":201,"y":245,"width":171,"height":118,"stroke":"#F300F9","listening":false,"state":null,"lx":372,"ly":363},{"id":2,"x":172,"y":71,"width":169,"height":122,"stroke":"#00F939","listening":false,"state":null,"lx":341,"ly":193}]}]
\ No newline at end of file
{
"name": "backend",
"version": "1.0.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"version": "1.0.0",
"license": "ISC",
"dependencies": {
"body-parser": "^1.19.0",
"connect-history-api-fallback": "^1.6.0",
"dotenv": "^10.0.0",
"express": "^4.17.1"
}
},
"node_modules/accepts": {
"version": "1.3.7",
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz",
"integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==",
"dependencies": {
"mime-types": "~2.1.24",
"negotiator": "0.6.2"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/array-flatten": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
"integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
},
"node_modules/body-parser": {
"version": "1.19.0",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz",
"integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==",
"dependencies": {
"bytes": "3.1.0",
"content-type": "~1.0.4",
"debug": "2.6.9",
"depd": "~1.1.2",
"http-errors": "1.7.2",
"iconv-lite": "0.4.24",
"on-finished": "~2.3.0",
"qs": "6.7.0",
"raw-body": "2.4.0",
"type-is": "~1.6.17"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/bytes": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz",
"integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/connect-history-api-fallback": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz",
"integrity": "sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==",
"engines": {
"node": ">=0.8"
}
},
"node_modules/content-disposition": {
"version": "0.5.3",
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz",
"integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==",
"dependencies": {
"safe-buffer": "5.1.2"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/content-type": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
"integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/cookie": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz",
"integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/cookie-signature": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
"integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
},
"node_modules/debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"dependencies": {
"ms": "2.0.0"
}
},
"node_modules/depd": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
"integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/destroy": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
"integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA="
},
"node_modules/dotenv": {
"version": "10.0.0",
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz",
"integrity": "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==",
"engines": {
"node": ">=10"
}
},
"node_modules/ee-first": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
"integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
},
"node_modules/encodeurl": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
"integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/escape-html": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
"integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg="
},
"node_modules/etag": {
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
"integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/express": {
"version": "4.17.1",
"resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz",
"integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==",
"dependencies": {
"accepts": "~1.3.7",
"array-flatten": "1.1.1",
"body-parser": "1.19.0",
"content-disposition": "0.5.3",
"content-type": "~1.0.4",
"cookie": "0.4.0",
"cookie-signature": "1.0.6",
"debug": "2.6.9",
"depd": "~1.1.2",
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"etag": "~1.8.1",
"finalhandler": "~1.1.2",
"fresh": "0.5.2",
"merge-descriptors": "1.0.1",
"methods": "~1.1.2",
"on-finished": "~2.3.0",
"parseurl": "~1.3.3",
"path-to-regexp": "0.1.7",
"proxy-addr": "~2.0.5",
"qs": "6.7.0",
"range-parser": "~1.2.1",
"safe-buffer": "5.1.2",
"send": "0.17.1",
"serve-static": "1.14.1",
"setprototypeof": "1.1.1",
"statuses": "~1.5.0",
"type-is": "~1.6.18",
"utils-merge": "1.0.1",
"vary": "~1.1.2"
},
"engines": {
"node": ">= 0.10.0"
}
},
"node_modules/finalhandler": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz",
"integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==",
"dependencies": {
"debug": "2.6.9",
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"on-finished": "~2.3.0",
"parseurl": "~1.3.3",
"statuses": "~1.5.0",
"unpipe": "~1.0.0"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/forwarded": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
"integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/fresh": {
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
"integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/http-errors": {
"version": "1.7.2",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz",
"integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==",
"dependencies": {
"depd": "~1.1.2",
"inherits": "2.0.3",
"setprototypeof": "1.1.1",
"statuses": ">= 1.5.0 < 2",
"toidentifier": "1.0.0"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/iconv-lite": {
"version": "0.4.24",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
"dependencies": {
"safer-buffer": ">= 2.1.2 < 3"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/inherits": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
},
"node_modules/ipaddr.js": {
"version": "1.9.1",
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
"integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
"engines": {
"node": ">= 0.10"
}
},
"node_modules/media-typer": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
"integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/merge-descriptors": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
"integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E="
},
"node_modules/methods": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
"integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/mime": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
"integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
"bin": {
"mime": "cli.js"
},
"engines": {
"node": ">=4"
}
},
"node_modules/mime-db": {
"version": "1.44.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz",
"integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/mime-types": {
"version": "2.1.27",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz",
"integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==",
"dependencies": {
"mime-db": "1.44.0"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
},
"node_modules/negotiator": {
"version": "0.6.2",
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
"integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/on-finished": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
"integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
"dependencies": {
"ee-first": "1.1.1"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/parseurl": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
"integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/path-to-regexp": {
"version": "0.1.7",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
"integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w="
},
"node_modules/proxy-addr": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz",
"integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==",
"dependencies": {
"forwarded": "~0.1.2",
"ipaddr.js": "1.9.1"
},
"engines": {
"node": ">= 0.10"
}
},
"node_modules/qs": {
"version": "6.7.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz",
"integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==",
"engines": {
"node": ">=0.6"
}
},
"node_modules/range-parser": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
"integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/raw-body": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz",
"integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==",
"dependencies": {
"bytes": "3.1.0",
"http-errors": "1.7.2",
"iconv-lite": "0.4.24",
"unpipe": "1.0.0"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
},
"node_modules/safer-buffer": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
},
"node_modules/send": {
"version": "0.17.1",
"resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz",
"integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==",
"dependencies": {
"debug": "2.6.9",
"depd": "~1.1.2",
"destroy": "~1.0.4",
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"etag": "~1.8.1",
"fresh": "0.5.2",
"http-errors": "~1.7.2",
"mime": "1.6.0",
"ms": "2.1.1",
"on-finished": "~2.3.0",
"range-parser": "~1.2.1",
"statuses": "~1.5.0"
},
"engines": {
"node": ">= 0.8.0"
}
},
"node_modules/send/node_modules/ms": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
"integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg=="
},
"node_modules/serve-static": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz",
"integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==",
"dependencies": {
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"parseurl": "~1.3.3",
"send": "0.17.1"
},
"engines": {
"node": ">= 0.8.0"
}
},
"node_modules/setprototypeof": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz",
"integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw=="
},
"node_modules/statuses": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
"integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/toidentifier": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz",
"integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==",
"engines": {
"node": ">=0.6"
}
},
"node_modules/type-is": {
"version": "1.6.18",
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
"integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
"dependencies": {
"media-typer": "0.3.0",
"mime-types": "~2.1.24"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/unpipe": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
"integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/utils-merge": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
"integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=",
"engines": {
"node": ">= 0.4.0"
}
},
"node_modules/vary": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
"integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=",
"engines": {
"node": ">= 0.8"
}
}
},
"dependencies": {
"accepts": {
"version": "1.3.7",
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz",
"integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==",
"requires": {
"mime-types": "~2.1.24",
"negotiator": "0.6.2"
}
},
"array-flatten": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
"integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
},
"body-parser": {
"version": "1.19.0",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz",
"integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==",
"requires": {
"bytes": "3.1.0",
"content-type": "~1.0.4",
"debug": "2.6.9",
"depd": "~1.1.2",
"http-errors": "1.7.2",
"iconv-lite": "0.4.24",
"on-finished": "~2.3.0",
"qs": "6.7.0",
"raw-body": "2.4.0",
"type-is": "~1.6.17"
}
},
"bytes": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz",
"integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg=="
},
"connect-history-api-fallback": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz",
"integrity": "sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg=="
},
"content-disposition": {
"version": "0.5.3",
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz",
"integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==",
"requires": {
"safe-buffer": "5.1.2"
}
},
"content-type": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
"integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA=="
},
"cookie": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz",
"integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg=="
},
"cookie-signature": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
"integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
},
"debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"requires": {
"ms": "2.0.0"
}
},
"depd": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
"integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak="
},
"destroy": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
"integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA="
},
"dotenv": {
"version": "10.0.0",
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz",
"integrity": "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q=="
},
"ee-first": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
"integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
},
"encodeurl": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
"integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k="
},
"escape-html": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
"integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg="
},
"etag": {
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
"integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc="
},
"express": {
"version": "4.17.1",
"resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz",
"integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==",
"requires": {
"accepts": "~1.3.7",
"array-flatten": "1.1.1",
"body-parser": "1.19.0",
"content-disposition": "0.5.3",
"content-type": "~1.0.4",
"cookie": "0.4.0",
"cookie-signature": "1.0.6",
"debug": "2.6.9",
"depd": "~1.1.2",
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"etag": "~1.8.1",
"finalhandler": "~1.1.2",
"fresh": "0.5.2",
"merge-descriptors": "1.0.1",
"methods": "~1.1.2",
"on-finished": "~2.3.0",
"parseurl": "~1.3.3",
"path-to-regexp": "0.1.7",
"proxy-addr": "~2.0.5",
"qs": "6.7.0",
"range-parser": "~1.2.1",
"safe-buffer": "5.1.2",
"send": "0.17.1",
"serve-static": "1.14.1",
"setprototypeof": "1.1.1",
"statuses": "~1.5.0",
"type-is": "~1.6.18",
"utils-merge": "1.0.1",
"vary": "~1.1.2"
}
},
"finalhandler": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz",
"integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==",
"requires": {
"debug": "2.6.9",
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"on-finished": "~2.3.0",
"parseurl": "~1.3.3",
"statuses": "~1.5.0",
"unpipe": "~1.0.0"
}
},
"forwarded": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
"integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ="
},
"fresh": {
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
"integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac="
},
"http-errors": {
"version": "1.7.2",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz",
"integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==",
"requires": {
"depd": "~1.1.2",
"inherits": "2.0.3",
"setprototypeof": "1.1.1",
"statuses": ">= 1.5.0 < 2",
"toidentifier": "1.0.0"
}
},
"iconv-lite": {
"version": "0.4.24",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
"requires": {
"safer-buffer": ">= 2.1.2 < 3"
}
},
"inherits": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
},
"ipaddr.js": {
"version": "1.9.1",
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
"integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="
},
"media-typer": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
"integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g="
},
"merge-descriptors": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
"integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E="
},
"methods": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
"integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4="
},
"mime": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
"integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="
},
"mime-db": {
"version": "1.44.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz",
"integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg=="
},
"mime-types": {
"version": "2.1.27",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz",
"integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==",
"requires": {
"mime-db": "1.44.0"
}
},
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
},
"negotiator": {
"version": "0.6.2",
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
"integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw=="
},
"on-finished": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
"integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
"requires": {
"ee-first": "1.1.1"
}
},
"parseurl": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
"integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="
},
"path-to-regexp": {
"version": "0.1.7",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
"integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w="
},
"proxy-addr": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz",
"integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==",
"requires": {
"forwarded": "~0.1.2",
"ipaddr.js": "1.9.1"
}
},
"qs": {
"version": "6.7.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz",
"integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ=="
},
"range-parser": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
"integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="
},
"raw-body": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz",
"integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==",
"requires": {
"bytes": "3.1.0",
"http-errors": "1.7.2",
"iconv-lite": "0.4.24",
"unpipe": "1.0.0"
}
},
"safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
},
"safer-buffer": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
},
"send": {
"version": "0.17.1",
"resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz",
"integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==",
"requires": {
"debug": "2.6.9",
"depd": "~1.1.2",
"destroy": "~1.0.4",
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"etag": "~1.8.1",
"fresh": "0.5.2",
"http-errors": "~1.7.2",
"mime": "1.6.0",
"ms": "2.1.1",
"on-finished": "~2.3.0",
"range-parser": "~1.2.1",
"statuses": "~1.5.0"
},
"dependencies": {
"ms": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
"integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg=="
}
}
},
"serve-static": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz",
"integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==",
"requires": {
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"parseurl": "~1.3.3",
"send": "0.17.1"
}
},
"setprototypeof": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz",
"integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw=="
},
"statuses": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
"integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow="
},
"toidentifier": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz",
"integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw=="
},
"type-is": {
"version": "1.6.18",
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
"integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
"requires": {
"media-typer": "0.3.0",
"mime-types": "~2.1.24"
}
},
"unpipe": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
"integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw="
},
"utils-merge": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
"integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM="
},
"vary": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
"integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw="
}
}
}
{
"name": "backend",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "node app.js"
},
"author": "",
"license": "ISC",
"dependencies": {
"body-parser": "^1.19.0",
"connect-history-api-fallback": "^1.6.0",
"dotenv": "^10.0.0",
"express": "^4.17.1"
}
}
.DS_Store
node_modules
/dist
/tests/e2e/reports/
selenium-debug.log
chromedriver.log
geckodriver.log
# local env files
.env.local
.env.*.local
# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
# front-end
## Project setup
```
npm install
```
### Compiles and hot-reloads for development
```
npm run serve
```
### Compiles and minifies for production
```
npm run build
```
### Run your unit tests
```
npm run test:unit
```
### Run your end-to-end tests
```
npm run test:e2e
```
### Lints and fixes files
```
npm run lint
```
### Customize configuration
See [Configuration Reference](https://cli.vuejs.org/config/).
module.exports = {
presets: [
'@vue/cli-plugin-babel/preset'
]
}
This source diff could not be displayed because it is too large. You can view the blob instead.
{
"name": "frontend",
"version": "0.1.0",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"test:unit": "vue-cli-service test:unit",
"test:e2e": "vue-cli-service test:e2e",
"lint": "vue-cli-service lint",
"start": "npm run serve",
"build2": "npm run build"
},
"dependencies": {
"chart.js": "^2.9.4",
"core-js": "^3.6.5",
"konva": "^7.1.3",
"register-service-worker": "^1.7.1",
"vue": "^2.6.11",
"vue-chartjs": "^3.5.1",
"vue-konva": "^2.1.6",
"vue-router": "^3.2.0",
"vuetify": "^2.2.11",
"vuex": "^3.4.0"
},
"devDependencies": {
"@mdi/js": "^5.5.55",
"@vue/cli-plugin-babel": "~4.5.0",
"@vue/cli-plugin-e2e-nightwatch": "~4.5.0",
"@vue/cli-plugin-eslint": "~4.5.0",
"@vue/cli-plugin-pwa": "~4.5.0",
"@vue/cli-plugin-router": "~4.5.0",
"@vue/cli-plugin-unit-jest": "~4.5.0",
"@vue/cli-plugin-vuex": "~4.5.0",
"@vue/cli-service": "~4.5.0",
"@vue/test-utils": "^1.0.3",
"babel-eslint": "^10.1.0",
"chromedriver": "85",
"eslint": "^6.7.2",
"eslint-plugin-vue": "^6.2.2",
"sass": "^1.26.5",
"sass-loader": "^8.0.2",
"vue-cli-plugin-vuetify": "~2.0.7",
"vue-template-compiler": "^2.6.11",
"vuetify-loader": "^1.3.0"
},
"eslintConfig": {
"root": true,
"env": {
"node": true
},
"extends": [
"plugin:vue/essential",
"eslint:recommended"
],
"parserOptions": {
"parser": "babel-eslint"
},
"rules": {},
"overrides": [
{
"files": [
"**/__tests__/*.{j,t}s?(x)",
"**/tests/unit/**/*.spec.{j,t}s?(x)"
],
"env": {
"jest": true
}
}
]
},
"browserslist": [
"> 1%",
"last 2 versions",
"not dead"
],
"jest": {
"preset": "@vue/cli-plugin-unit-jest"
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title><%= htmlWebpackPlugin.options.title %></title>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@mdi/font@latest/css/materialdesignicons.min.css">
</head>
<body>
<noscript>
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
</body>
</html>
[
{
"project_num":1,
"total_num":0,
"current_done":0,
"current_reject":0,
"image_path":"image/test1",
"start_date":"2020-08-31",
"end_date":"2020-09-31"
},
{
"project_num":2,
"total_num":0,
"current_done":0,
"current_reject":0,
"image_path":"image/test2",
"start_date":"2020-09-31",
"end_date":"2020-10-31"
},
{
"project_num":3,
"total_num":0,
"current_done":0,
"current_reject":0,
"image_path":"image/test3",
"start_date":"2020-07-12",
"end_date":"2020-09-31"
}
]
\ No newline at end of file
User-agent: *
Disallow:
<template>
<v-app>
<v-main>
<Alert
:open="alertOpen"
:message="message"
:type="type"
/>
<!-- <Progress
:update="progressUpdate"
:open="progressOpen"
:number="progressNumber"
:label="progressLabel"
/> -->
<router-view/>
</v-main>
</v-app>
</template>
<script>
import EventBus from './event-bus'
import Alert from './components/Alert.vue'
export default {
name: 'App',
components: {
Alert
},
created() {
EventBus.$on('openAlert',( massage , type) => {
this.alertOpen = true,
this.message = massage,
this.type = type
}),
EventBus.$on('closeAlert',() => {
this.alertOpen = false
})
},
data: () => ({
alertOpen: false ,
message:'',
type:'',
}),
mounted(){
this.$store.dispatch('getProjectList')
},
};
</script>
<style lang="scss">
:root{
/* Color */
--admin-color: #0091ea;
--user-color: #338a3e; //338a3e
--white : #f2f2f2;
--gray: #6A79A6;
--red: #ff1744;
/* Font Size */
--font-l: 24px;
--font-r: 20px;
--font-m: 18px;
--font-s: 16px;
--font-mc: 14px;
}
html,body{
padding: 0;
margin: 0;
height: 100%;
width: 100%
}
#app {
height: 100%;
width: 100%;
}
</style>
<template>
<v-overlay
:value="openAlert"
:z-index="20"
>
<v-alert
:type="type"
dismissible
@click="closeAlert()"
>
{{ message }}
</v-alert>
</v-overlay>
</template>
<script>
import EventBus from '../event-bus'
export default {
props: {
open:Boolean,
message:String,
type:String,
},
watch: {
open(newVal) {
if (newVal) {
setTimeout(() => {
this.closeAlert()
}, 2000);
}
},
},
computed: {
openAlert: {
get() {
return this.open;
},
set() {
this.closeAlert()
},
},
},
methods:{
closeAlert(){
EventBus.$emit("closeAlert");
}
}
};
</script>
\ No newline at end of file
<script>
// import VueCharts from 'vue-chartjs'
import { Doughnut,mixins } from 'vue-chartjs'
const { reactiveProp } = mixins
//https://www.chartjs.org/docs/latest/charts/line.html 옵션
//https://www.chartjs.org/samples/latest/
export default {
extends: Doughnut, //차트 종류 ex)bar
mixins: [ reactiveProp] ,
props:{
chartdata:Object,
percent: Number,
},
data () {
return {
options: {
title: {
display: false,
// text: 'title'
},
legend: {
position: 'bottom',
display: true,
},
responsive: true,
maintainAspectRatio: false
},
}
},
mounted () {
this.addPlugin({
id: 'my-plugin',
beforeDraw: this.centerText
})
this.renderChart(this.chartData, this.options )
},
methods: {
//가운데 퍼센트 표시하기 위함
centerText(chart){
let width = chart.chart.width;
let height = chart.chart.height;
let ctx = chart.chart.ctx;
ctx.restore();
let fontSize = (height / 150).toFixed(2);
ctx.font = fontSize + "em sans-serif" ;
ctx.textBaseline = "middle";
let text = this.percent+'%';
let textX = Math.round((width - ctx.measureText(text).width) / 2);
let textY = height / 2.2;
ctx.fillText(text, textX, textY);
ctx.save();
}
}
}
</script>
\ No newline at end of file
import Vue from 'vue'
const eventBus = new Vue({})
export default eventBus
import Vue from 'vue'
import App from './App.vue'
import './registerServiceWorker'
import router from './router'
import store from './store'
import vuetify from './plugins/vuetify';
import axios from 'axios'
import VueKonva from 'vue-konva'
Vue.use(VueKonva)
axios.defaults.baseURL = '/api' //모든 요청에 '/api' 붙도록 기본 설정
axios.defaults.headers.common.Accept = 'application/json'//JSON 형식으로만 받는다
axios.interceptors.response.use( //Error 전파하기 위해 인터셉터 응답을 추가한다.
response => response,
(error) => {
return Promise.reject(error)
}
)
//Vue.prototype.$http = axios //vue 컴포넌트에서 this.$http 요청할 수 있게 된다.
Vue.config.productionTip = false
new Vue({
router,
store,
vuetify,
render: h => h(App)
}).$mount('#app')
<template>
<v-overlay v-if="open" :z-index="10">
<v-card width="1200" color="grey lighten-3" class="d-flex flex-column mx-auto">
<v-toolbar
color="#66bb6a"
elevation="0"
>
<v-toolbar-title class="font-weight-black">
Teaching
</v-toolbar-title>
</v-toolbar>
<v-card-text>
<v-row>
<v-col cols="6">
<Info
:counter="totalCounter"
:info="info"
:list="teachingList"
/>
</v-col>
<v-col>
<Stepper
:counter="totalCounter"
:step="step"
:roading="roading"
:errorMessage="errorMessage"
:percent="percent"
:fail="failResult"
:success="successResult"
:fpga="fpgaResult"
:response="responseResult"
/>
</v-col>
</v-row>
</v-card-text>
<v-card-actions>
<v-spacer/>
<v-btn
class="font-weight-bold"
text
color="primary"
:disabled="step === 5 ? false : true"
@click="save()"
>
save
</v-btn>
<v-btn
light
text
:disabled="step === 5 || step === 1 ? false : true"
@click="close()"
>
close
</v-btn>
</v-card-actions>
</v-card>
</v-overlay>
</template>
<script>
import EventBus from '../event-bus'
import Info from './TeachungModal/Info.vue'
import Stepper from './TeachungModal/Stepper.vue'
import teachingService from '@/service/teaching'
export default {
props: {
open:Boolean,
info:Object,
totalCounter:Number
},
data () {
return {
step:1,
counter:0,
roading:false,
errorMessage:'',
teachingList: [],
failResult:0,
successResult:0,
fpgaResult:0,
responseResult:0,
percent:0
}
},
components:{
Info,
Stepper
},
created () {
EventBus.$on('start',() =>{ this.neuroStart()})
EventBus.$on('retry',(step) => {this.retry(step)})
},
methods:{
retry(step){
this.requestInit()
switch (step){
case 2:
this.neuroReady()
break;
case 3:
this.neuroCapture()
break;
case 4:
this.neuroCheck()
break;
case 5:
this.step = 1
this.teachingList = []
this.counter = 0
break;
}
},
requestFail(message){
this.roading = false
this.errorMessage = `Error : ${message}`
},
requestInit(){
this.roading = true
this.errorMessage = ''
},
async neuroStart(){
let msg = new Object()
msg.cmd = "neuro_start"
msg.project_num = this.info.project_num
await teachingService.requestManager(msg)
.then(data => {
console.log(data)
this.step = 2
})
.catch(error =>{
console.log(error.response)
error.response.data !== undefined ? this.requestFail(error.response.data) : this.requestFail(error.response.status)
})
},
async neuroReady(){
let msg = new Object()
msg.cmd = "neuro_ready"
await teachingService.requestManager(msg)
.then(data => {
console.log(data)
this.step = 3
})
.catch(error =>{
console.log(error.response)
error.response.data !== undefined ? this.requestFail(error.response.data) : this.requestFail(error.response.status)
})
},
async neuroCapture(){
let msg = new Object()
msg.cmd = "neuro_capture"
await teachingService.requestManager(msg)
.then(data => {
console.log(data)
this.step = 4
})
.catch(error =>{
console.log(error.response)
error.response.data !== undefined ? this.requestFail(error.response.data) : this.requestFail(error.response.status)
})
},
async neuroCheck(){
let msg = new Object()
msg.cmd = "neuro_check"
msg.project_num = this.info.project_num
msg.teaching_info ='(1113,720,1329,840)-(1626,504,1788,651)-(1953,816,2049,978)'
await teachingService.requestManager(msg)
.then(data => {
this.counter = ++this.counter
let teaching = JSON.parse(data.data)
let result = new Object()
result.counter = this.counter
result.state = teaching['result']
result.response = parseFloat(teaching['proc_time'])
result.fpga = 0
this.teachingList.push(result)
this.counter === this.totalCounter ? this.step = 5 : this.step = 2
})
.catch(error =>{
error.response.data !== undefined ? this.requestFail(error.response.data) : this.requestFail(error.response.status)
})
},
resultCounter(){
this.failResult = 0
this.successResult = 0
this.fpgaResult = 0
this.responseResult = 0
this.percent =0
let stateArr = this.teachingList.map(teaching => { return teaching['state']})
let responseArr = this.teachingList.map(teaching => { return teaching['response']})
let fpgaArr = this.teachingList.map(teaching => { return teaching['fpga'] })
stateArr.forEach(state => { state === 0 ? ++this.failResult : ++this.successResult})
this.fpgaResult = fpgaArr.reduce((stack, el)=>{ return stack + el }, 0)
this.responseResult = responseArr.reduce((stack, el)=>{ return stack + el}, 0)
this.fpgaResult = this.fpgaResult > 0 ? Number(this.fpgaResult.toFixed(7)) : 0
this.responseResult =this.responseResult > 0 ? Number(this.responseResult.toFixed(7)) : 0
this.percent = this.success / this.totalCounter * 100 > 0 ? Number((this.success/ this.totalCounter * 100).toFixed(0)) : 0
},
close(){
this.counter = 0
this.step = 1
this.teachingList = []
EventBus.$emit('projectModalClose')
}
},
watch:{
'step': function(newVal){
if(newVal === 2){
this.requestInit()
this.neuroReady()
}else if(newVal === 3){
this.requestInit()
this.neuroCapture()
}else if(newVal === 4){
this.requestInit()
this.neuroCheck()
}else if(newVal === 5){
this.resultCounter()
}
}
},
beforeDestroy(){
EventBus.$off('start');
EventBus.$off('requestRetry');
}
}
</script>
<style>
</style>
\ No newline at end of file
<template>
<v-card
light
max-height="600"
min-height="600"
>
<v-card-text>
<v-row no-gutters>
<v-col cols='6' class="ma-0 pa-0">
<div class="text-h5 font-weight-black">{{info.project_name}}</div>
<p>{{info.start_date}} - {{info.end_date}} </p>
<p>관리자 <span>: {{info.project_admin }}</span></p>
<p>작업자 <span >: {{info.project_user }}</span></p>
<p>검수 횟수 :<span class="primary--text font-weight-bold"> {{counter }}</span></p>
</v-col>
<v-col cols='6' class="ma-0 pa-0">
<v-img
:src="host"
contain
width="350"
height="190"
color="grey"
>
</v-img>
</v-col>
</v-row>
<v-data-table
light
height="300"
max-height="300"
min-height="300"
:headers="headers"
:items="list"
:items-per-page="5"
no-data-text="검수 이력이 없습니다."
class="elevation-1 mt-5"
:sort-by="['counter']"
:sort-desc="[true]"
:footer-props="{
'items-per-page-text':'페이지 수'
}"
>
<template v-slot:[`item.state`]="{ item }">
<v-chip
:color="item.state === 0 ? 'error' : 'success'"
dark
>
{{ item.state === 0 ? 'Fail' : 'Success' }}
</v-chip>
</template>
</v-data-table>
</v-card-text>
</v-card>
</template>
<script>
export default {
props: {
info:Object,
list:Array,
counter:Number,
},
data () {
return {
headers: [
{ text: '횟수', align: 'start', sortable: false, value: 'counter',},
{ text: '상태', value: 'state' },
{ text: '응답 속도', value: 'response' },
{ text: 'FPGA 속도', value: 'fpga' },
],
}
},
computed:{
host(){
let protocol = location.protocol;
let hostName = location.hostname === 'localhost' ? '192.168.53.196': location.hostname
return protocol+'//'+hostName+':'+8081
}
},
}
</script>
<style>
</style>
\ No newline at end of file
<template>
<v-stepper v-model="step" light>
<v-stepper-header >
<v-stepper-step color="#98ee99" :complete="step > 1" step="1"><span class="font-weight-bold">START</span></v-stepper-step>
<v-divider></v-divider>
<v-stepper-step color="#98ee99" :complete="step > 2" step="2"><span class="font-weight-bold">SETTING</span> </v-stepper-step>
<v-divider></v-divider>
<v-stepper-step color="#98ee99" :complete="step > 3" step="3"><span class="font-weight-bold">CAPTURE</span> </v-stepper-step>
<v-divider></v-divider>
<v-stepper-step color="#98ee99" :complete="step > 4" step="4"><span class="font-weight-bold">TEACHING</span></v-stepper-step>
<v-divider></v-divider>
<v-stepper-step color="#98ee99" step="5"><span class="font-weight-bold">END</span></v-stepper-step>
</v-stepper-header>
<v-stepper-items>
<v-stepper-content step="1" >
<v-card
class="mb-12"
elevation="0"
height="405px"
>
<v-card-text >
<v-timeline
dense
>
<v-timeline-item
v-for="line in timeline"
:key="line.id"
>
<template v-slot:icon>
<v-btn small fab color="#98ee99" elevation="0"><span class="white--text"> {{line.id}}</span></v-btn>
</template>
<v-row class="pt-1">
<v-col cols="3">
<strong>{{line.title}}</strong>
</v-col>
<v-col>
<strong>{{line.text}}</strong>
<div class="text-caption">
{{line.caption}}
</div>
</v-col>
</v-row>
</v-timeline-item>
</v-timeline>
</v-card-text>
</v-card>
<v-row no-gutters>
<v-spacer/>
<v-btn
outlined
color="primary"
@click="teachingStart()"
>
start
</v-btn>
</v-row>
</v-stepper-content>
<v-stepper-content step="2">
<v-card
class="mb-12"
elevation="0"
height="405px"
>
<v-card-text class="headline font-weight-bold grey--text text-center " >
<v-progress-circular
v-if="roading"
:size="300"
:width="20"
color="primary"
indeterminate
> <div class="headline font-weight-bold primary--text">Capture...</div></v-progress-circular>
<div v-else class="font-weight-bold #009624--text">{{errorMessage}}</div>
</v-card-text>
</v-card>
<v-row no-gutters>
<v-spacer/>
<v-btn
outlined
color="primary"
:disabled="roading"
@click="requestRetry(step)"
>
retry
</v-btn>
</v-row>
</v-stepper-content>
<v-stepper-content step="3">
<v-card
class="mb-12"
elevation="0"
height="405px"
>
<v-card-text class="headline font-weight-bold grey--text text-center " >
<v-progress-circular
v-if="roading"
:size="300"
:width="20"
color="primary"
indeterminate
> <div class="headline font-weight-bold primary--text">Capture...</div></v-progress-circular>
<div v-else class="font-weight-bold #009624--text">{{errorMessage}}</div>
</v-card-text>
</v-card>
<v-row no-gutters>
<v-spacer/>
<v-btn
outlined
color="primary"
:disabled="roading"
@click="requestRetry(step)"
>
retry
</v-btn>
</v-row>
</v-stepper-content>
<v-stepper-content step="4">
<v-card
class="mb-12"
elevation="0"
height="405px"
>
<v-card-text class="headline font-weight-bold grey--text text-center " >
<v-progress-circular
v-if="roading"
:size="300"
:width="20"
color="primary"
indeterminate
> <div class="headline font-weight-bold primary--text">Teaching...</div></v-progress-circular>
<div v-else class="font-weight-bold #009624--text">{{errorMessage}}</div>
</v-card-text>
</v-card>
<v-row no-gutters>
<v-spacer/>
<v-btn
outlined
color="primary"
:disabled="roading"
@click="requestRetry(step)"
>
retry
</v-btn>
</v-row>
</v-stepper-content>
<v-stepper-content step="5">
<v-card
class="mb-12"
elevation="0"
height="405px"
>
<v-card-text class="headline font-weight-bold grey--text" >
<p class="black--text text-center">결과 </p>
<v-row>
<v-col cols="6" class="title font-weight-bold text-center">검수 성공률</v-col>
<v-col cols="6" class="title font-weight-bold text-center">검수 속도</v-col>
</v-row>
<v-row>
<v-col cols="6">
<DoughnutChart
v-if="step === 5"
:chart-data="totalColl"
:height="200"
:percent=percent
/>
</v-col>
<v-col cols="6">
<v-simple-table dense>
<template v-slot:default>
<thead>
<tr>
<th class="text-left">
속도
</th>
<th class="text-left">
합계
</th>
</tr>
</thead>
<tbody>
<tr
>
<td>응답</td>
<td>{{response}}</td>
</tr>
<tr
>
<td>FPGA</td>
<td>{{fpga}}</td>
</tr>
</tbody>
</template>
</v-simple-table>
</v-col>
</v-row>
</v-card-text>
</v-card>
<v-row no-gutters>
<v-spacer/>
<v-btn
outlined
color="primary"
@click="requestRetry(step)"
>
retry
</v-btn>
</v-row>
</v-stepper-content>
</v-stepper-items>
</v-stepper>
</template>
<script>
import EventBus from '../../event-bus'
import DoughnutChart from '../../components/DoughnutChart.vue'
export default {
props: {
step:Number,
counter:Number,
roading:Boolean,
errorMessage:String,
fail:Number,
success:Number,
fpga:Number,
response:Number,
percent:Number,
},
data () {
return {
timeline:[
{id:1,title:"START",text:`'START' 버튼을 시작하면 검수를 시작합니다.` , caption:`${this.counter} 회 자동 실행 됩니다.`},
{id:2,title:"SETTING",text:`검수할 보드를 세팅 합니다.`,caption:``},
{id:3,title:"CAPTURE",text:`보드를 찍습니다.`,caption:``},
{id:4,title:"TEACHING",text:`보드를 검수합니다.`, caption:``},
{id:5,title:"END",text:`검수 결과를 확인합니다.`, caption:`성공,실패 유무 및 응답 속도 `},
],
totalColl:{}
}
},
components: {
DoughnutChart,
},
methods:{
teachingStart(){
EventBus.$emit('start')
},
requestRetry(step){
EventBus.$emit('retry',step)
}
},
watch:{
step(val){
if(val === 5){
this.totalColl = {
labels: ['성공','실패'],
datasets: [
{
backgroundColor: ['#4CAF50' ,'#F44336'],
data: [ this.success ,this.fail],
}
],
}
}
}
}
}
</script>
<style>
</style>
\ No newline at end of file
import Vue from 'vue';
import Vuetify from 'vuetify/lib';
Vue.use(Vuetify);
export default new Vuetify({
icons: {
iconfont: 'mdiSvg',
},
});
/* eslint-disable no-console */
import { register } from 'register-service-worker'
if (process.env.NODE_ENV === 'production') {
register(`${process.env.BASE_URL}service-worker.js`, {
ready () {
console.log(
'App is being served from cache by a service worker.\n' +
'For more details, visit https://goo.gl/AFskqB'
)
},
registered () {
console.log('Service worker has been registered.')
},
cached () {
console.log('Content has been cached for offline use.')
},
updatefound () {
console.log('New content is downloading.')
},
updated () {
console.log('New content is available; please refresh.')
},
offline () {
console.log('No internet connection found. App is running in offline mode.')
},
error (error) {
console.error('Error during service worker registration:', error)
}
})
}
import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '../views/Home.vue'
import List from '../views/User/List.vue'
Vue.use(VueRouter)
const routes = [
{
path: '/',
name: 'home',
component: Home
},
{
path: '/list',
name: 'list',
component: List
},
]
const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes
})
export default router
import axios from 'axios'
export default {
async projectCreateNum() {
try{
const result = await axios.get('/projectCreateNum')
return result.data
} catch (error) {
return error.message
}
},
async projectCreate(data) {
try{
const result = await axios.post('/projectCreate',data)
return result
} catch (error) {
return error.message
}
},
async projectCapture (data) {
try{
const result = await axios.get('/requestManager',{ params: data })
return result
} catch (error) {
return error.message
}
},
// axios.get('/neuroCapture',{ params: param })
projectUpdate (data) {
console.log("파라미터 : " ,data)
return new Promise((resolve,reject) =>{
// axios.post('/projectModify',{ params: data })
axios.post('/projectUpdate',data)
.then(({data}) => {
resolve(data)})
.catch((error) => {
reject(new Error(`${error} 요청 실패입니다.`));
})
})
},
projectRemove (data) {
console.log("파라미터 : " ,data)
return new Promise((resolve,reject) =>{
// axios.post('/projectModify',{ params: data })
axios.post('/projectUpdate',data)
.then(({data}) => {
resolve(data)})
.catch((error) => {
reject(new Error(`${error} 요청 실패입니다.`));
})
})
}
}
\ No newline at end of file
export default{
neuroStart (param){
return new Promise((resolve,reject) => {
console.log("param :", param)
console.log("param.cmd :",JSON.parse(param).cmd)
JSON.parse(param).cmd === "neuro_start" ? resolve(param) : reject(new Error("요청 값이 틀립니다."))
})
},
neuroCapture (param){
return new Promise((resolve,reject) => {
console.log("param :", param)
console.log("param.cmd :",JSON.parse(param).cmd)
JSON.parse(testJson).cmd === "capture_done" ? resolve(testJson) : reject(new Error("요청 값이 틀립니다."))
})
},
neuroCheck (param){
return new Promise((resolve,reject) => {
console.log("param :", param)
console.log("JSON.parse(param).cmd :",JSON.parse(param).cmd)
JSON.parse(param).cmd === "neuro_check" ? resolve("검수 성공 ") : reject(new Error("요청 값이 틀립니다."))
})
},
neuroSave (param){
return new Promise((resolve,reject) => {
console.log("param :", param)
JSON.parse(testJson).cmd === "capture_done" ? resolve(testJson) : reject(new Error("요청 값이 틀립니다."))
})
},
}
\ No newline at end of file
import axios from 'axios'
export default {
getProjectList () {
return new Promise((resolve,reject) =>{
axios.get('/getProjectList')
.then(({data}) => {
resolve(data)})
.catch((error) => {
reject(new Error(`${error} 요청 실패입니다.`));
})
})
},
projectModify (data) {
console.log("파라미터 : " ,data)
return new Promise((resolve,reject) =>{
axios.post('/teachingInfoModify',data)
.then(({data}) => {
resolve(data)})
.catch((error) => {
reject(new Error(`${error} 요청 실패입니다.`));
})
})
}
}
\ No newline at end of file
import axios from 'axios'
/* {data} 받으면 data.data. => data. 접근 가능 */
export default{
async requestManager (param) {
try{
const result = await axios.get('/requestManager',{ params: param })
return Promise.resolve(result)
} catch (error) {
return Promise.reject(error)
}
},
}
\ No newline at end of file
import projectService from '@/service/project'
export const getProjectList = ({commit}) => {
projectService.getProjectList()
.then(data => {
commit('projectList',data)
})
}
export const modifyPoject = ({commit}, info) => {
commit('modifyPoject', info)
}
export const projectUpdate = ({commit}, updateData) => {
commit('projectUpdate', updateData)
}
export const projectAdd = ({commit}, newData) => {
commit('projectAdd', newData)
}
export const projectList = state => state.projectList
export const projectListS = state => {
return state.projectList.filter(s => s.project_state === "success")
}
export const projectListW = state => {
return state.projectList.filter(w => w.project_state === "waiting")
}
export const projectListP = state => {
return state.projectList.filter(p => p.project_state === "proceeding")
}
export const projectListC = state => {
return state.projectList.filter(c => c.project_state === "")
}
import Vue from 'vue'
import Vuex from 'vuex'
import * as getters from './getters'
import * as actions from './actions'
import mutations from './mutations'
import createLogger from 'vuex/dist/logger'
/* state 값을 사용할 떄는 부모 컴포넌트, 공통 컴포넌트 에서 관리해야한다.*/
Vue.use(Vuex)
const state = {
projectList : [],
}
export default new Vuex.Store({
state,
getters,
mutations,
actions,
plugins: process.env.NODE_ENV !== 'production'
? [createLogger()]
: []
})
\ No newline at end of file
export default {
projectList (state , data){
// state.projectList.push(data)
state.projectList = data
},
modifyPoject(state , payload){
for(let i in state.projectList){
if(state.projectList[i].project_num === payload.project_num){
state.projectList[i].current_done = payload.current_done
state.projectList[i].current_reject = payload.current_reject
state.projectList[i].current_counter = payload.current_counter
// state.projectList[i].total_num = payload.total_num
}
}
},
projectUpdate(state, payload){
for(let i in state.projectList){
if(state.projectList[i].project_num === payload.project_num){
if(payload.msg === "update"){
state.projectList[i].project_admin = payload.project_admin
state.projectList[i].project_user = payload.project_user
state.projectList[i].total_num = payload.total_num
state.projectList[i].start_date = payload.start_date
state.projectList[i].end_date = payload.end_date
}else{
state.projectList[i].project_state = "remove"
}
}
}
},
projectAdd(state,payload){
state.projectList.push(payload)
}
}
\ No newline at end of file
<template>
<div class="select-page">
<div class="select-page-admin">
<div class="content">
<h1>뉴로모픽<br>관리자</h1>
<p>프로젝트를 생성하고 관리한다.<br><br></p>
<a href="https://www.nemopai.com/neuro/single/neuro/">시작</a>
</div>
</div>
<div class="select-page-user">
<div class="content">
<h1>뉴로모픽<br>사용자</h1>
<p>관리자가 만든 프로젝트를 선택하여<br>검수를 시작한다.<br> </p>
<router-link class="user" to="/list">시작</router-link>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'Home',
}
</script>
<style lang="scss">
/* common */
.content {
font-family: 'Source Sans Pro';
color: var(--white);
line-height: 1.5; /* 줄의 높이*/
text-align: center;
img{
max-width: 100px;
min-height: 100px;
}
h1{
font-size: 60px;
}
a{
background-color: var(--white);
border-radius: 50px;
line-height: 50px; /* a태그 높이는 지정 */
width: 200px;
display: inline-block;
margin: 20px 0;
font-size: var(--font-large);
font-weight: bold;
font-size: var(--font-l);
text-decoration: none;
}
}
.select-page{
display: flex;
height: 100vh;
.select-page-admin{
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
background-color: var(--admin-color);
width: 100%;
height: 100%;
// &:hover{
// background-color: #9cff57;
// }
.content{
a{
color:var(--admin-color);
}
}
}
.select-page-user{
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
width: 100%;
height: 100%;
background-color: var(--user-color);
// &:hover{
// background-color: #9cff57;
// }
.content{
a{
color:var(--user-color);
}
}
}
}
</style>
\ No newline at end of file
<template>
<div class="pj-list d-flex justify-center align-center">
<v-container class="d-flex justify-center align-center">
<v-card
width="1800"
min-height="800"
color="grey lighten-3"
>
<v-toolbar
color="#66bb6a"
elevation="0"
>
<v-toolbar-title class="font-weight-black white--text" >
PROJECT
</v-toolbar-title>
</v-toolbar>
<v-card-text>
<v-row>
<v-col cols="6">
<List
:projectList="projectList"
/>
</v-col>
<v-col cols="6">
<No v-if="Object.keys(selectedProject).length === 0 && JSON.stringify(selectedProject) === JSON.stringify({})"/>
<Info v-else
:selectedProject="selectedProject"
/>
</v-col>
</v-row>
</v-card-text>
</v-card>
</v-container>
<TeachingModal
:open="teachingModal"
:info="teachingInfo"
:totalCounter="teachingCounter"
/>
</div>
</template>
<script>
import { mapGetters } from 'vuex'
import teachingService from '@/service/teaching'
import EventBus from '../../event-bus'
import List from '../User/List/List.vue'
import Info from '../User/List/Info.vue'
import No from './List/No.vue'
import TeachingModal from '@/modal/TeachingModal'
export default {
data () {
return {
projectHeader: [
{
text: '이름',
align: 'start',
sortable: false,
value: 'project_name',
},
{ text: '관리자', value: 'project_admin', sortable: false,},
{ text: '사용자', value: 'project_user', sortable: false,},
{ text: '생성일자', value: 'start_date', sortable: false,},
{ text: '완료일자', value: 'end_date', sortable: false, },
{ text: '조회', value: 'actions', sortable: false },
],
selectedProject:{},
teachingInfo:{},
teachingCounter:0,
teachingModal:false
}
},
components: {
TeachingModal,
List,
Info,
No
},
created () {
EventBus.$on('projectSelected',(project) =>{
console.log(project)
this.selectedProject = project
})
EventBus.$on('projectListReload',() =>{
this.selectedProject = {}
this.reload()
})
EventBus.$on('projectTeachingStart',(project,counter) => {
this.teachingStart(project,counter)
})
EventBus.$on('projectModalClose',() => {
this.teachingModal = false
})
},
computed: {
...mapGetters([
'projectList'
]),
},
methods: {
reload(){
this.$store.dispatch('getProjectList')
},
async teachingStart(project,counter){
this.teachingInfo = project,
this.teachingCounter = counter,
this.teachingModal = true
// const msg = new Object()
// msg.cmd = "neuro_start"
// msg.project_num = project.project_num
// await teachingService.requestManager(msg)
// .then((response) => {
// console.log(response)
// })
// .catch((error) => {return EventBus.$emit('openAlert',"ERROR : "+ error , 'error') })
// .finally(() => { this.$store.dispatch('offLoading')})
},
start (item) {
console.log("save 아이템 : "+JSON.stringify(item))
const startTeaching=confirm('작업을 시작하시겠습니까?')
if(startTeaching){
const msg = new Object()
msg.cmd = "neuro_start"
msg.project_num = item.project_num
this.$router.push({ name: 'teaching', params: item});
teachingService.requestManager(msg)
this.close()
}
},
},
}
</script>
<style lang="scss">
.pj-list{
background-color: var(--user-color);
height: 100vh;
}
</style>
\ No newline at end of file
<template>
<v-card class="d-flex flex-column" height="770" light>
<v-card-title class="text-h5 font-weight-black">
{{selectedProject.project_name}}
</v-card-title>
<v-card-subtitle>
{{selectedProject.start_date}} - {{selectedProject.end_date}}
</v-card-subtitle>
<v-card-text>
<v-row>
<v-col>
<v-text-field
label="관리자"
readonly
outlined
:value="selectedProject.project_admin"
>
</v-text-field>
</v-col>
<v-col>
<v-text-field
label="사용자"
readonly
outlined
:value="selectedProject.project_admin"
>
</v-text-field>
</v-col>
<v-col>
<v-text-field
label="검수 횟수"
ref="counterInput"
v-model.number="selectedProject.total_num"
outlined
type="number"
min="0"
>
</v-text-field>
</v-col>
</v-row>
<v-row>
<v-img
height="500"
width="500"
:src="selectedProject.image_path"
></v-img>
</v-row>
</v-card-text>
<v-card-actions>
<v-spacer/>
<v-btn
class="font-weight-bold"
outlined
color="primary"
@click="teaching(selectedProject)"
>
Teaching
</v-btn>
</v-card-actions>
</v-card>
</template>
<script>
import EventBus from '../../../event-bus'
export default {
props:{
selectedProject:Object
},
methods:{
teaching(project){
if(this.selectedProject.total_num === 0){
EventBus.$emit('openAlert',`검수 횟수를 '0' 이상의 값을 입력해주세요 ` , 'error')
return this.$refs.counterInput.focus()
}else{
EventBus.$emit('projectTeachingStart',project,this.selectedProject.total_num)
}
}
}
}
</script>
<style>
</style>
\ No newline at end of file
<template>
<v-card max-height="770" light>
<v-toolbar
color="#98ee99"
>
<v-toolbar-title class="font-weight-bold white--text ">
List
</v-toolbar-title>
<v-spacer></v-spacer>
<v-btn icon @click="reload()">
<v-icon>mdi-refresh</v-icon>
</v-btn>
</v-toolbar>
<v-data-table
height="646"
max-height="646"
min-height="646"
:headers="projectHeader"
:items="projectList"
no-data-text="프로젝트가 없습니다"
sort-by="createDate"
class="elevation-1"
>
<template v-slot:[`item.actions`]="{ item }">
<v-chip
color="#80d8ff"
@click="selected(item)"
dark
>
선택
</v-chip>
</template>
</v-data-table>
</v-card>
</template>
<script>
import EventBus from '../../../event-bus'
export default {
data () {
return {
projectHeader: [
{
text: '이름',
align: 'start',
sortable: false,
value: 'project_name',
},
{ text: '관리자', value: 'project_admin', sortable: false,},
{ text: '사용자', value: 'project_user', sortable: false,},
{ text: '생성일자', value: 'start_date', sortable: false,},
{ text: '완료일자', value: 'end_date', sortable: false, },
{ text: '조회', value: 'actions', sortable: false },
],
}
},
props:{
projectList:Array,
},
methods:{
reload(){
EventBus.$emit('projectListReload')
},
selected(project){
EventBus.$emit('projectSelected',project)
}
}
}
</script>
<style>
</style>
\ No newline at end of file
<template>
<v-card class="d-flex justify-center align-center" color="grey lighten-2" height="770">
<div class="title font-weight-bold">조회 하실 프로젝트를 선택해주세요</div>
</v-card>
</template>
<script>
export default {
}
</script>
<style>
</style>
\ No newline at end of file
module.exports = {
rules: {
'no-unused-expressions': 'off'
}
}
/**
* A custom Nightwatch assertion. The assertion name is the filename.
*
* Example usage:
* browser.assert.elementCount(selector, count)
*
* For more information on custom assertions see:
* https://nightwatchjs.org/guide/extending-nightwatch/#writing-custom-assertions
*
*
* @param {string|object} selectorOrObject
* @param {number} count
*/
exports.assertion = function elementCount (selectorOrObject, count) {
let selector
// when called from a page object element or section
if (typeof selectorOrObject === 'object' && selectorOrObject.selector) {
// eslint-disable-next-line prefer-destructuring
selector = selectorOrObject.selector
} else {
selector = selectorOrObject
}
this.message = `Testing if element <${selector}> has count: ${count}`
this.expected = count
this.pass = val => val === count
this.value = res => res.value
function evaluator (_selector) {
return document.querySelectorAll(_selector).length
}
this.command = cb => this.api.execute(evaluator, [selector], cb)
}
/**
* A very basic Nightwatch custom command. The command name is the filename and the
* exported "command" function is the command.
*
* Example usage:
* browser.customExecute(function() {
* console.log('Hello from the browser window')
* });
*
* For more information on writing custom commands see:
* https://nightwatchjs.org/guide/extending-nightwatch/#writing-custom-commands
*
* @param {*} data
*/
exports.command = function command (data) {
// Other Nightwatch commands are available via "this"
// .execute() inject a snippet of JavaScript into the page for execution.
// the executed script is assumed to be synchronous.
//
// See https://nightwatchjs.org/api/execute.html for more info.
//
this.execute(
// The function argument is converted to a string and sent to the browser
function (argData) { return argData },
// The arguments for the function to be sent to the browser are specified in this array
[data],
function (result) {
// The "result" object contains the result of what we have sent back from the browser window
console.log('custom execute result:', result.value)
}
)
return this
}
/**
* A basic Nightwatch custom command
* which demonstrates usage of ES6 async/await instead of using callbacks.
* The command name is the filename and the exported "command" function is the command.
*
* Example usage:
* browser.openHomepage();
*
* For more information on writing custom commands see:
* https://nightwatchjs.org/guide/extending-nightwatch/#writing-custom-commands
*
*/
module.exports = {
command: async function () {
// Other Nightwatch commands are available via "this"
// .init() simply calls .url() command with the value of the "launch_url" setting
this.init()
this.waitForElementVisible('#app')
const result = await this.elements('css selector', '#app ul')
this.assert.strictEqual(result.value.length, 3)
}
}
/**
* A class-based Nightwatch custom command which is a variation of the openHomepage.js command.
* The command name is the filename and class needs to contain a "command" method.
*
* Example usage:
* browser.openHomepageClass();
*
* For more information on writing custom commands see:
* https://nightwatchjs.org/guide/extending-nightwatch/#writing-custom-commands
*
*/
const assert = require('assert')
module.exports = class {
async command () {
// Other Nightwatch commands are available via "this.api"
this.api.init()
this.api.waitForElementVisible('#app')
const result = await this.api.elements('css selector', '#app ul')
assert.strictEqual(result.value.length, 3)
}
}
///////////////////////////////////////////////////////////////////////////////////
// Refer to the entire list of global config settings here:
// https://github.com/nightwatchjs/nightwatch/blob/master/lib/settings/defaults.js#L16
//
// More info on test globals:
// https://nightwatchjs.org/gettingstarted/configuration/#test-globals
//
///////////////////////////////////////////////////////////////////////////////////
module.exports = {
// this controls whether to abort the test execution when an assertion failed and skip the rest
// it's being used in waitFor commands and expect assertions
abortOnAssertionFailure: true,
// this will overwrite the default polling interval (currently 500ms) for waitFor commands
// and expect assertions that use retry
waitForConditionPollInterval: 500,
// default timeout value in milliseconds for waitFor commands and implicit waitFor value for
// expect assertions
waitForConditionTimeout: 5000,
'default': {
/*
The globals defined here are available everywhere in any test env
*/
/*
myGlobal: function() {
return 'I\'m a method';
}
*/
},
'firefox': {
/*
The globals defined here are available only when the chrome testing env is being used
i.e. when running with --env firefox
*/
/*
* myGlobal: function() {
* return 'Firefox specific global';
* }
*/
},
/////////////////////////////////////////////////////////////////
// Global hooks
// - simple functions which are executed as part of the test run
// - take a callback argument which can be called when an async
// async operation is finished
/////////////////////////////////////////////////////////////////
/**
* executed before the test run has started, so before a session is created
*/
/*
before(cb) {
//console.log('global before')
cb();
},
*/
/**
* executed before every test suite has started
*/
/*
beforeEach(browser, cb) {
//console.log('global beforeEach')
cb();
},
*/
/**
* executed after every test suite has ended
*/
/*
afterEach(browser, cb) {
browser.perform(function() {
//console.log('global afterEach')
cb();
});
},
*/
/**
* executed after the test run has finished
*/
/*
after(cb) {
//console.log('global after')
cb();
},
*/
/////////////////////////////////////////////////////////////////
// Global reporter
// - define your own custom reporter
/////////////////////////////////////////////////////////////////
/*
reporter(results, cb) {
cb();
}
*/
}
/**
* A Nightwatch page object. The page object name is the filename.
*
* Example usage:
* browser.page.homepage.navigate()
*
* For more information on working with page objects see:
* https://nightwatchjs.org/guide/working-with-page-objects/
*
*/
module.exports = {
url: '/',
commands: [],
// A page object can have elements
elements: {
appContainer: '#app'
},
// Or a page objects can also have sections
sections: {
app: {
selector: '#app',
elements: {
logo: 'img'
},
// - a page object section can also have sub-sections
// - elements or sub-sections located here are retrieved using the "app" section as the base
sections: {
headline: {
selector: 'h1'
},
welcome: {
// the equivalent css selector for the "welcome" sub-section would be:
// '#app div.hello'
selector: 'div.hello',
elements: {
cliPluginLinks: {
selector: 'ul',
index: 0
}
}
}
}
}
}
}
////////////////////////////////////////////////////////////////
// For authoring Nightwatch tests, see
// https://nightwatchjs.org/guide
//
// For more information on working with page objects see:
// https://nightwatchjs.org/guide/working-with-page-objects/
////////////////////////////////////////////////////////////////
module.exports = {
beforeEach: (browser) => browser.init(),
'e2e tests using page objects': (browser) => {
const homepage = browser.page.homepage()
homepage.waitForElementVisible('@appContainer')
const app = homepage.section.app
app.assert.elementCount('@logo', 1)
app.expect.section('@welcome').to.be.visible
app.expect.section('@headline').text.to.match(/^Welcome to Your Vue\.js (.*)App$/)
browser.end()
},
'verify if string "e2e-nightwatch" is within the cli plugin links': (browser) => {
const homepage = browser.page.homepage()
const welcomeSection = homepage.section.app.section.welcome
welcomeSection.expect.element('@cliPluginLinks').text.to.contain('e2e-nightwatch')
}
}
// For authoring Nightwatch tests, see
// https://nightwatchjs.org/guide
module.exports = {
'default e2e tests': browser => {
browser
.init()
.waitForElementVisible('#app')
.assert.elementPresent('.hello')
.assert.containsText('h1', 'Welcome to Your Vue.js App')
.assert.elementCount('img', 1)
.end()
},
'example e2e test using a custom command': browser => {
browser
.openHomepage()
.assert.elementPresent('.hello')
.end()
}
}
import { shallowMount } from '@vue/test-utils'
import HelloWorld from '@/components/HelloWorld.vue'
describe('HelloWorld.vue', () => {
it('renders props.msg when passed', () => {
const msg = 'new message'
const wrapper = shallowMount(HelloWorld, {
propsData: { msg }
})
expect(wrapper.text()).toMatch(msg)
})
})
module.exports = {
"transpileDependencies": [
"vuetify"
],
devServer: {
proxy: {
'/api/*': {
target: 'http://localhost:3000' // 개발서버
}
}
},
//npm run build 하면 지정된 경로로 생성한다.
outputDir: '../backend/public' ,
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment