diff --git a/app.js b/app.js index 835a54e..d073571 100644 --- a/app.js +++ b/app.js @@ -9,6 +9,7 @@ const config = require('./config'); const so = require('./includes/storeObject'); const exec = require('child_process').exec; const fs = require('fs'); +const archiver = require('archiver'); const app = express(); @@ -63,13 +64,13 @@ app.get('/camevent', (req, res) => { 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 img = req.params.img; - if (!dir || !img) return res.sendStatus(404); + let file = req.params.file; + if (!dir || !file) return res.sendStatus(404); try { 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); return resolve(stats); }) @@ -78,7 +79,39 @@ app.get('/viewcamimg/:dir/:img', async(req, res) => { } catch (e) { 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) => { diff --git a/includes/language/zh.json b/includes/language/zh.json index e2bd22c..d1b2cce 100644 --- a/includes/language/zh.json +++ b/includes/language/zh.json @@ -72,6 +72,7 @@ "ERR0070": "IPCam 裝置已達上限", "ERR0071": "無此裝置資料", "ERR0072": "目錄資訊取得失敗", + "ERR0073": "目錄輸入錯誤", "ERR7000": "命令執行失敗", diff --git a/package.json b/package.json index 895c07f..1d68337 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "author": "", "license": "MIT", "dependencies": { + "archiver": "^1.3.0", "body-parser": "^1.16.1", "cookie-parser": "^1.4.3", "cors": "^2.8.1", diff --git a/route/api/ipcam.js b/route/api/ipcam.js index 80d8356..60daa35 100644 --- a/route/api/ipcam.js +++ b/route/api/ipcam.js @@ -285,6 +285,38 @@ router } 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); module.exports = router; \ No newline at end of file diff --git a/src/components/AdminPage/IPCam/EventModal.js b/src/components/AdminPage/IPCam/EventModal.js index 5fbbb47..1f91af7 100644 --- a/src/components/AdminPage/IPCam/EventModal.js +++ b/src/components/AdminPage/IPCam/EventModal.js @@ -1,9 +1,9 @@ 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 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 ( {closeEventModal()}} size="fullscreen"> @@ -26,8 +26,17 @@ const EventModal = ({ i18n, open, name, list, sel, closeEventModal, changeSelect let n = t.name; let time = n.split('-')[1]; return ( - {changeSelectEvent(idx)}}> - {convertTime(time, true)} + + {changeSelectEvent(idx)}} + style={{cursor: "pointer"}}>{convertTime(time, true)} + {window.open(`/dlevent/${t.name}`, '_blank')}}/> + {delEvent(t.name)}}/> )}) } @@ -36,6 +45,25 @@ const EventModal = ({ i18n, open, name, list, sel, closeEventModal, changeSelect + { + sel != -1 && list[sel].files.video.length > 0 ? ( + + 影片下載 +

+ { + list[sel].files.video.map((t, idx) => { + + return ( +

+ {t} +
+ ) + }) + } +

+
+ ) :null + } { sel != -1 ? ( diff --git a/src/components/AdminPage/IPCam/IPCamModal.js b/src/components/AdminPage/IPCam/IPCamModal.js index 2ca7357..d3aa4f2 100644 --- a/src/components/AdminPage/IPCam/IPCamModal.js +++ b/src/components/AdminPage/IPCam/IPCamModal.js @@ -46,10 +46,10 @@ const IPCamModal = ({ i18n, open, type, data, closeModal, submitModal }) => { - + - + diff --git a/src/components/AdminPage/IPCam/index.js b/src/components/AdminPage/IPCam/index.js index 2a1f3be..f2122da 100644 --- a/src/components/AdminPage/IPCam/index.js +++ b/src/components/AdminPage/IPCam/index.js @@ -77,8 +77,8 @@ class IPCamPage extends React.Component { submitModal = (type, data = {}) => { let {toggleLoading,showDialog} = this.props; if(type == 1 && !data.id) return showDialog('資料讀取錯誤'); - if(!data.maxevents || data.maxevents < 1 || data.maxevents > 5) return showDialog('事件數量請介於1-5間'); - if(!data.maximg || data.maximg < 1 || data.maximg > 10) 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 > 20) return showDialog('圖片數量請介於1-5間'); if(!data.name) return showDialog('請輸入名稱'); if(!data.ip) return showDialog('請輸入IP'); 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() { let {i18n} = this.props; return ( @@ -197,7 +212,8 @@ class IPCamPage extends React.Component { name={this.state.evt.name} refreshEvt={this.getEvents} closeEventModal={this.closeEventModal} - changeSelectEvent={this.changeSelectEvent} /> + changeSelectEvent={this.changeSelectEvent} + delEvent={this.delEvent} /> ) }