Merge branch 'master' into release
This commit is contained in:
commit
97cb15bdb1
47
app.js
47
app.js
@ -9,6 +9,7 @@ const config = require('./config');
|
|||||||
const so = require('./includes/storeObject');
|
const so = require('./includes/storeObject');
|
||||||
const exec = require('child_process').exec;
|
const exec = require('child_process').exec;
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
|
const archiver = require('archiver');
|
||||||
|
|
||||||
const app = express();
|
const app = express();
|
||||||
|
|
||||||
@ -63,22 +64,54 @@ app.get('/camevent', (req, res) => {
|
|||||||
res.send({ ip });
|
res.send({ ip });
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
app.get('/viewcamimg/:dir/:img', async(req, res) => {
|
app.get(['/viewcamimg/:dir/:file', '/dlcamvideo/:dir/:file'], async(req, res) => {
|
||||||
let dir = req.params.dir;
|
let dir = req.params.dir;
|
||||||
let img = req.params.img;
|
let file = req.params.file;
|
||||||
if (!dir || !img) return res.status(404).end();
|
if (!dir || !file) return res.sendStatus(404);
|
||||||
try {
|
try {
|
||||||
let stat = await new Promise((resolve, reject) => {
|
let stat = await new Promise((resolve, reject) => {
|
||||||
fs.stat(path.resolve(config.cmdpath.ipcamsave, dir, img), (err, stats) => {
|
fs.stat(path.resolve(config.cmdpath.ipcamsave, dir, file), (err, stats) => {
|
||||||
if (err) return reject(err);
|
if (err) return reject(err);
|
||||||
return resolve(stats);
|
return resolve(stats);
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
if (!stat.isFile()) return res.status(404).end();
|
if (!stat.isFile()) return res.sendStatus(404);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return res.status(404).end();
|
return res.sendStatus(404);
|
||||||
}
|
}
|
||||||
res.sendfile(path.resolve(config.cmdpath.ipcamsave, dir, img))
|
res.sendfile(path.resolve(config.cmdpath.ipcamsave, dir, file))
|
||||||
|
})
|
||||||
|
app.get('/dlevent/:dir', async(req, res) => {
|
||||||
|
let dir = req.params.dir;
|
||||||
|
if (!dir) return res.sendStatus(404);
|
||||||
|
let list = [];
|
||||||
|
try {
|
||||||
|
let stat = await new Promise((resolve, reject) => {
|
||||||
|
fs.stat(path.resolve(config.cmdpath.ipcamsave, dir), (err, stats) => {
|
||||||
|
if (err) return reject(err);
|
||||||
|
return resolve(stats);
|
||||||
|
})
|
||||||
|
});
|
||||||
|
if (!stat.isDirectory()) return res.sendStatus(404);
|
||||||
|
list = await new Promise((resolve, reject) => {
|
||||||
|
fs.readdir(path.resolve(config.cmdpath.ipcamsave, dir), (err, fis) => {
|
||||||
|
if (err) return reject(err);
|
||||||
|
return resolve(fis);
|
||||||
|
})
|
||||||
|
})
|
||||||
|
} catch (e) {
|
||||||
|
return res.sendStatus(404);
|
||||||
|
}
|
||||||
|
res.writeHead(200, {
|
||||||
|
'Content-Type': 'application/zip',
|
||||||
|
'Content-disposition': 'attachment; filename=cam_event.zip'
|
||||||
|
});
|
||||||
|
let zip = archiver('zip', { store: true });
|
||||||
|
zip.pipe(res);
|
||||||
|
for (let i of list) {
|
||||||
|
zip.file(path.resolve(config.cmdpath.ipcamsave, dir, i), { name: i })
|
||||||
|
}
|
||||||
|
zip.finalize();
|
||||||
})
|
})
|
||||||
|
|
||||||
app.get('/servcmd', (req, res) => {
|
app.get('/servcmd', (req, res) => {
|
||||||
|
@ -72,6 +72,7 @@
|
|||||||
"ERR0070": "IPCam 裝置已達上限",
|
"ERR0070": "IPCam 裝置已達上限",
|
||||||
"ERR0071": "無此裝置資料",
|
"ERR0071": "無此裝置資料",
|
||||||
"ERR0072": "目錄資訊取得失敗",
|
"ERR0072": "目錄資訊取得失敗",
|
||||||
|
"ERR0073": "目錄輸入錯誤",
|
||||||
|
|
||||||
"ERR7000": "命令執行失敗",
|
"ERR7000": "命令執行失敗",
|
||||||
|
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
"author": "",
|
"author": "",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"archiver": "^1.3.0",
|
||||||
"body-parser": "^1.16.1",
|
"body-parser": "^1.16.1",
|
||||||
"cookie-parser": "^1.4.3",
|
"cookie-parser": "^1.4.3",
|
||||||
"cors": "^2.8.1",
|
"cors": "^2.8.1",
|
||||||
|
@ -285,6 +285,38 @@ router
|
|||||||
}
|
}
|
||||||
return n();
|
return n();
|
||||||
})
|
})
|
||||||
|
.post('/delevent', async(req, res, n) => {
|
||||||
|
if (!config.permission.ipcam) return n('ERR9000');
|
||||||
|
if (!tool.checkPermission(req)) return n('ERR9000');
|
||||||
|
let arr = req.body;
|
||||||
|
if (!arr.data) return n('ERR0000');
|
||||||
|
if (!arr.data.dir) return n('ERR0073');
|
||||||
|
|
||||||
|
let rp = config.cmdpath.ipcamsave;
|
||||||
|
let dp = path.resolve(rp, arr.data.dir);
|
||||||
|
try {
|
||||||
|
let stat = await new Promise((resolve, reject) => {
|
||||||
|
fs.stat(dp, (err, stats) => {
|
||||||
|
if (err) return reject(err);
|
||||||
|
return resolve(stats);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
if (dp.split(' ')[0] == '/') return n('ERR0073');
|
||||||
|
await new Promise((resolve, reject) => {
|
||||||
|
exec(`rm -rf ${dp}`, (err, sout, serr) => {
|
||||||
|
if (err) return reject(err);
|
||||||
|
return resolve(null);
|
||||||
|
})
|
||||||
|
})
|
||||||
|
} catch (e) {
|
||||||
|
return rt.err(res, e, n, 'ERR0072');
|
||||||
|
}
|
||||||
|
|
||||||
|
res.api_res = {
|
||||||
|
record: []
|
||||||
|
}
|
||||||
|
return n();
|
||||||
|
})
|
||||||
.all('*', rt.send);
|
.all('*', rt.send);
|
||||||
|
|
||||||
module.exports = router;
|
module.exports = router;
|
@ -1,9 +1,9 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Modal, Grid, List, Menu, Icon } from 'semantic-ui-react';
|
import { Modal, Grid, List, Menu, Icon, Message } from 'semantic-ui-react';
|
||||||
import {convertTime} from '../../../tools';
|
import {convertTime} from '../../../tools';
|
||||||
import ImgGrid from './EvtImgGrid';
|
import ImgGrid from './EvtImgGrid';
|
||||||
|
|
||||||
const EventModal = ({ i18n, open, name, list, sel, closeEventModal, changeSelectEvent,refreshEvt }) => {
|
const EventModal = ({ i18n, open, name, list, sel, closeEventModal, changeSelectEvent,refreshEvt, delEvent }) => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal open={open} onClose={()=>{closeEventModal()}} size="fullscreen">
|
<Modal open={open} onClose={()=>{closeEventModal()}} size="fullscreen">
|
||||||
@ -26,8 +26,17 @@ const EventModal = ({ i18n, open, name, list, sel, closeEventModal, changeSelect
|
|||||||
let n = t.name;
|
let n = t.name;
|
||||||
let time = n.split('-')[1];
|
let time = n.split('-')[1];
|
||||||
return (
|
return (
|
||||||
<Menu.Item key={idx} active={sel == idx} onClick={()=>{changeSelectEvent(idx)}}>
|
<Menu.Item key={idx} active={sel == idx} >
|
||||||
{convertTime(time, true)}
|
<span onClick={()=>{changeSelectEvent(idx)}}
|
||||||
|
style={{cursor: "pointer"}}>{convertTime(time, true)}</span>
|
||||||
|
<Icon name="download" title="下載事件" style={{
|
||||||
|
cursor: "pointer",
|
||||||
|
float: 'right'
|
||||||
|
}} onClick={()=>{window.open(`/dlevent/${t.name}`, '_blank')}}/>
|
||||||
|
<Icon name="trash" title="刪除事件" style={{
|
||||||
|
cursor: "pointer",
|
||||||
|
float: 'right'
|
||||||
|
}} onClick={()=>{delEvent(t.name)}}/>
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
)})
|
)})
|
||||||
}
|
}
|
||||||
@ -36,6 +45,25 @@ const EventModal = ({ i18n, open, name, list, sel, closeEventModal, changeSelect
|
|||||||
</Menu>
|
</Menu>
|
||||||
</Grid.Column>
|
</Grid.Column>
|
||||||
<Grid.Column width={12}>
|
<Grid.Column width={12}>
|
||||||
|
{
|
||||||
|
sel != -1 && list[sel].files.video.length > 0 ? (
|
||||||
|
<Message>
|
||||||
|
<Message.Header>影片下載</Message.Header>
|
||||||
|
<p>
|
||||||
|
{
|
||||||
|
list[sel].files.video.map((t, idx) => {
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div key={idx}>
|
||||||
|
<a href={`/dlcamvideo/${list[sel].name}/${t}`} target="_blank">{t}</a>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</p>
|
||||||
|
</Message>
|
||||||
|
) :null
|
||||||
|
}
|
||||||
{
|
{
|
||||||
sel != -1 ?
|
sel != -1 ?
|
||||||
(
|
(
|
||||||
|
@ -46,10 +46,10 @@ const IPCamModal = ({ i18n, open, type, data, closeModal, submitModal }) => {
|
|||||||
</select>
|
</select>
|
||||||
</Form.Field>
|
</Form.Field>
|
||||||
<Form.Field>
|
<Form.Field>
|
||||||
<Input name="maxevents" label="最大儲存事件數量(1-5)" defaultValue={data.maxevents} />
|
<Input name="maxevents" label="最大儲存事件數量(1-10)" defaultValue={data.maxevents} />
|
||||||
</Form.Field>
|
</Form.Field>
|
||||||
<Form.Field>
|
<Form.Field>
|
||||||
<Input name="maximg" label="最大儲存圖片數量(1-10)" defaultValue={data.maximg} />
|
<Input name="maximg" label="最大儲存圖片數量(1-20)" defaultValue={data.maximg} />
|
||||||
</Form.Field>
|
</Form.Field>
|
||||||
<Grid columns={2}>
|
<Grid columns={2}>
|
||||||
<Grid.Column>
|
<Grid.Column>
|
||||||
|
@ -77,8 +77,8 @@ class IPCamPage extends React.Component {
|
|||||||
submitModal = (type, data = {}) => {
|
submitModal = (type, data = {}) => {
|
||||||
let {toggleLoading,showDialog} = this.props;
|
let {toggleLoading,showDialog} = this.props;
|
||||||
if(type == 1 && !data.id) return showDialog('資料讀取錯誤');
|
if(type == 1 && !data.id) return showDialog('資料讀取錯誤');
|
||||||
if(!data.maxevents || data.maxevents < 1 || data.maxevents > 5) return showDialog('事件數量請介於1-5間');
|
if(!data.maxevents || data.maxevents < 1 || data.maxevents > 10) return showDialog('事件數量請介於1-5間');
|
||||||
if(!data.maximg || data.maximg < 1 || data.maximg > 10) return showDialog('圖片數量請介於1-5間');
|
if(!data.maximg || data.maximg < 1 || data.maximg > 20) return showDialog('圖片數量請介於1-5間');
|
||||||
if(!data.name) return showDialog('請輸入名稱');
|
if(!data.name) return showDialog('請輸入名稱');
|
||||||
if(!data.ip) return showDialog('請輸入IP');
|
if(!data.ip) return showDialog('請輸入IP');
|
||||||
if(!data.model) return showDialog('請選擇型號');
|
if(!data.model) return showDialog('請選擇型號');
|
||||||
@ -146,6 +146,21 @@ class IPCamPage extends React.Component {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
delEvent = (dir) => {
|
||||||
|
if(!dir) return ;
|
||||||
|
let {callConfirm, toggleLoading, showDialog} = this.props;
|
||||||
|
callConfirm('確定刪除這筆事件?', ()=>{
|
||||||
|
toggleLoading(1);
|
||||||
|
fetch('/api/ipcam/delevent', getRequest({dir}))
|
||||||
|
.then(response=>response.json())
|
||||||
|
.then(json => {
|
||||||
|
toggleLoading(0);
|
||||||
|
if(json.status != 1) return showDialog(json.message);
|
||||||
|
this.getEvents(-1);
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let {i18n} = this.props;
|
let {i18n} = this.props;
|
||||||
return (
|
return (
|
||||||
@ -197,7 +212,8 @@ class IPCamPage extends React.Component {
|
|||||||
name={this.state.evt.name}
|
name={this.state.evt.name}
|
||||||
refreshEvt={this.getEvents}
|
refreshEvt={this.getEvents}
|
||||||
closeEventModal={this.closeEventModal}
|
closeEventModal={this.closeEventModal}
|
||||||
changeSelectEvent={this.changeSelectEvent} />
|
changeSelectEvent={this.changeSelectEvent}
|
||||||
|
delEvent={this.delEvent} />
|
||||||
</Container>
|
</Container>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user