diff --git a/frontend/.env.development b/frontend/.env.development index dade16fb..779d28e4 100644 --- a/frontend/.env.development +++ b/frontend/.env.development @@ -1,2 +1,2 @@ NODE_ENV='development' -VUE_APP_BASE_URL=http://localhost:8000/api +VUE_APP_BASE_URL=http://localhost:8000 diff --git a/frontend/package.json b/frontend/package.json index 7b75b711..ffeaa651 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -3,8 +3,8 @@ "version": "0.2.3", "private": true, "scripts": { - "serve": "cross-env NODE_ENV=development vue-cli-service serve --ip=0.0.0.0", - "serve:prod": "cross-env NODE_ENV=production vue-cli-service serve --mode=production --ip=0.0.0.0", + "serve": "vue-cli-service serve --ip=0.0.0.0", + "serve:prod": "vue-cli-service serve --mode=production --ip=0.0.0.0", "config": "vue ui", "build:prod": "vue-cli-service build --mode production", "lint": "vue-cli-service lint", diff --git a/frontend/src/components/DndList/index.vue b/frontend/src/components/DndList/index.vue deleted file mode 100644 index 7587daa7..00000000 --- a/frontend/src/components/DndList/index.vue +++ /dev/null @@ -1,157 +0,0 @@ - - - - {{ list1Title }} - - - {{ element.id }}[{{ element.author }}] {{ element.title }} - - - - - - - - - - {{ list2Title }} - - - {{ element.id }} [{{ element.author }}] {{ element.title }} - - - - - - - - - diff --git a/frontend/src/components/ErrorLog/index.vue b/frontend/src/components/ErrorLog/index.vue deleted file mode 100644 index c46cf110..00000000 --- a/frontend/src/components/ErrorLog/index.vue +++ /dev/null @@ -1,63 +0,0 @@ - - - - - - - - - - - - - - Msg: - {{ scope.row.err.message }} - - - - Info: - {{ scope.row.vm.$vnode.tag }} error in {{ scope.row.info }} - - - - Url: - {{ scope.row.url }} - - - - - - {{ scope.row.err.stack }} - - - - - - - - - - - diff --git a/frontend/src/components/GithubCorner/index.vue b/frontend/src/components/GithubCorner/index.vue deleted file mode 100644 index b5da8874..00000000 --- a/frontend/src/components/GithubCorner/index.vue +++ /dev/null @@ -1,51 +0,0 @@ - - - - - - - - - - - diff --git a/frontend/src/components/HeaderSearch/index.vue b/frontend/src/components/HeaderSearch/index.vue deleted file mode 100644 index ab0d556a..00000000 --- a/frontend/src/components/HeaderSearch/index.vue +++ /dev/null @@ -1,187 +0,0 @@ - - - - - - - - - - - - diff --git a/frontend/src/components/InfoView/NodeInfoView.vue b/frontend/src/components/InfoView/NodeInfoView.vue index bc6e687a..8e350448 100644 --- a/frontend/src/components/InfoView/NodeInfoView.vue +++ b/frontend/src/components/InfoView/NodeInfoView.vue @@ -7,13 +7,13 @@ class="node-form" label-position="right"> - + - + - - + + diff --git a/frontend/src/components/InfoView/SpiderInfoView.vue b/frontend/src/components/InfoView/SpiderInfoView.vue index d095f81a..a3ea417b 100644 --- a/frontend/src/components/InfoView/SpiderInfoView.vue +++ b/frontend/src/components/InfoView/SpiderInfoView.vue @@ -10,7 +10,7 @@ - + @@ -28,6 +28,7 @@ :placeholder="$t('Site')" :fetch-suggestions="fetchSiteSuggestions" clearable + :disabled="isView" @select="onSiteSelect"> @@ -49,7 +50,6 @@ {{$t('Run')}} - {{$t('Deploy')}} {{$t('Save')}} @@ -100,9 +100,6 @@ export default { isShowRun () { if (this.isCustomized) { // customized spider - if (!this.spiderForm.deploy_ts) { - return false - } return !!this.spiderForm.cmd } else { // configurable spider @@ -132,29 +129,6 @@ export default { } }) }, - onDeploy () { - const row = this.spiderForm - - // save spider - this.$store.dispatch('spider/editSpider', row._id) - - // validate fields - this.$refs['spiderForm'].validate(res => { - if (res) { - this.$confirm(this.$t('Are you sure to deploy this spider?'), this.$t('Notification'), { - confirmButtonText: this.$t('Confirm'), - cancelButtonText: this.$t('Cancel') - }) - .then(() => { - this.$store.dispatch('spider/deploySpider', row._id) - .then(() => { - this.$message.success(this.$t(`Spider has been deployed`)) - }) - this.$st.sendEv('爬虫详情-概览', '部署') - }) - } - }) - }, onSave () { this.$refs['spiderForm'].validate(res => { if (res) { diff --git a/frontend/src/components/InfoView/TaskInfoView.vue b/frontend/src/components/InfoView/TaskInfoView.vue index 92ad5869..a9c5d534 100644 --- a/frontend/src/components/InfoView/TaskInfoView.vue +++ b/frontend/src/components/InfoView/TaskInfoView.vue @@ -10,16 +10,10 @@ - {{$t('SUCCESS')}} - {{$t('STARTED')}} - {{$t('FAILURE')}} - {{$t(taskForm.status)}} + - - - - + @@ -37,9 +31,9 @@ - + - {{taskForm.log}} + {{ taskForm.error }} @@ -55,15 +49,17 @@ import { mapState } from 'vuex' +import StatusTag from '../Status/StatusTag' export default { name: 'NodeInfoView', + components: { StatusTag }, computed: { ...mapState('task', [ 'taskForm' ]), isRunning () { - return !['SUCCESS', 'FAILURE'].includes(this.taskForm.status) + return ['pending', 'running'].includes(this.taskForm.status) } }, methods: { diff --git a/frontend/src/components/JsonEditor/index.vue b/frontend/src/components/JsonEditor/index.vue deleted file mode 100644 index d68b8c05..00000000 --- a/frontend/src/components/JsonEditor/index.vue +++ /dev/null @@ -1,72 +0,0 @@ - - - - - - - - - diff --git a/frontend/src/components/Kanban/index.vue b/frontend/src/components/Kanban/index.vue deleted file mode 100644 index dadeb648..00000000 --- a/frontend/src/components/Kanban/index.vue +++ /dev/null @@ -1,89 +0,0 @@ - - - - {{ headerText }} - - - - {{ element.name }} {{ element.id }} - - - - - - - diff --git a/frontend/src/components/LangSelect/index.vue b/frontend/src/components/LangSelect/index.vue deleted file mode 100644 index fea7ba71..00000000 --- a/frontend/src/components/LangSelect/index.vue +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - 中文 - English - Español - - - - - diff --git a/frontend/src/components/MDinput/index.vue b/frontend/src/components/MDinput/index.vue deleted file mode 100644 index 7ede73db..00000000 --- a/frontend/src/components/MDinput/index.vue +++ /dev/null @@ -1,354 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - diff --git a/frontend/src/components/MarkdownEditor/defaultOptions.js b/frontend/src/components/MarkdownEditor/defaultOptions.js deleted file mode 100644 index 303aa13d..00000000 --- a/frontend/src/components/MarkdownEditor/defaultOptions.js +++ /dev/null @@ -1,31 +0,0 @@ -// doc: https://nhnent.github.io/tui.editor/api/latest/ToastUIEditor.html#ToastUIEditor -export default { - minHeight: '200px', - previewStyle: 'vertical', - useCommandShortcut: true, - useDefaultHTMLSanitizer: true, - usageStatistics: false, - hideModeSwitch: false, - toolbarItems: [ - 'heading', - 'bold', - 'italic', - 'strike', - 'divider', - 'hr', - 'quote', - 'divider', - 'ul', - 'ol', - 'task', - 'indent', - 'outdent', - 'divider', - 'table', - 'image', - 'link', - 'divider', - 'code', - 'codeblock' - ] -} diff --git a/frontend/src/components/MarkdownEditor/index.vue b/frontend/src/components/MarkdownEditor/index.vue deleted file mode 100644 index 4a6d8b0f..00000000 --- a/frontend/src/components/MarkdownEditor/index.vue +++ /dev/null @@ -1,118 +0,0 @@ - - - - - diff --git a/frontend/src/components/Overview/NodeOverview.vue b/frontend/src/components/Overview/NodeOverview.vue index e6412343..65773c74 100644 --- a/frontend/src/components/Overview/NodeOverview.vue +++ b/frontend/src/components/Overview/NodeOverview.vue @@ -5,11 +5,6 @@ - - - - - @@ -23,7 +18,6 @@ import { mapState } from 'vuex' -import DeployTableView from '../TableView/DeployTableView' import TaskTableView from '../TableView/TaskTableView' import NodeInfoView from '../InfoView/NodeInfoView' @@ -31,7 +25,6 @@ export default { name: 'NodeOverview', components: { NodeInfoView, - DeployTableView, TaskTableView }, computed: { diff --git a/frontend/src/components/Overview/SpiderOverview.vue b/frontend/src/components/Overview/SpiderOverview.vue index 52c265a6..1de562a6 100644 --- a/frontend/src/components/Overview/SpiderOverview.vue +++ b/frontend/src/components/Overview/SpiderOverview.vue @@ -5,11 +5,6 @@ - - - - - @@ -23,7 +18,6 @@ import { mapState } from 'vuex' -import DeployTableView from '../TableView/DeployTableView' import TaskTableView from '../TableView/TaskTableView' import SpiderInfoView from '../InfoView/SpiderInfoView' @@ -31,7 +25,6 @@ export default { name: 'SpiderOverview', components: { SpiderInfoView, - DeployTableView, TaskTableView }, data () { diff --git a/frontend/src/components/Status/StatusTag.vue b/frontend/src/components/Status/StatusTag.vue new file mode 100644 index 00000000..1f4f0c89 --- /dev/null +++ b/frontend/src/components/Status/StatusTag.vue @@ -0,0 +1,48 @@ + + + {{$t(label)}} + + + + + + diff --git a/frontend/src/components/TableView/TaskTableView.vue b/frontend/src/components/TableView/TaskTableView.vue index 7faefbce..afd61540 100644 --- a/frontend/src/components/TableView/TaskTableView.vue +++ b/frontend/src/components/TableView/TaskTableView.vue @@ -5,30 +5,29 @@ - + - {{scope.row.node_id}} + {{scope.row.node_name}} - + {{scope.row.spider_name}} - {{$t('SUCCESS')}} - {{$t('STARTED')}} - {{$t('FAILURE')}} - {{$t(scope.row['status'])}} + - + - {{scope.row.create_ts}} + + {{getTime(scope.row.create_ts).format('YYYY-MM-DD HH:mm:ss')}} + @@ -40,9 +39,12 @@ import { mapState } from 'vuex' +import dayjs from 'dayjs' +import StatusTag from '../Status/StatusTag' export default { name: 'TaskTableView', + components: { StatusTag }, data () { return { // setInterval handle @@ -76,6 +78,9 @@ export default { } else if (this.$route.path.split('/')[1] === 'nodes') { this.$store.dispatch('node/getTaskList', this.$route.params.id) } + }, + getTime (str) { + return dayjs(str) } }, mounted () { diff --git a/frontend/src/components/TextHoverEffect/Mallki.vue b/frontend/src/components/TextHoverEffect/Mallki.vue deleted file mode 100644 index 4ea29fc2..00000000 --- a/frontend/src/components/TextHoverEffect/Mallki.vue +++ /dev/null @@ -1,113 +0,0 @@ - - - {{ text }} - - - - - - - - diff --git a/frontend/src/components/TreeTable/eval.js b/frontend/src/components/TreeTable/eval.js deleted file mode 100644 index d9b89e1c..00000000 --- a/frontend/src/components/TreeTable/eval.js +++ /dev/null @@ -1,29 +0,0 @@ -/** -* @Author: jianglei -* @Date: 2017-10-12 12:06:49 -*/ -'use strict' -import Vue from 'vue' -export default function treeToArray(data, expandAll, parent = null, level = null) { - let tmp = [] - Array.from(data).forEach(function(record) { - if (record._expanded === undefined) { - Vue.set(record, '_expanded', expandAll) - } - let _level = 1 - if (level !== undefined && level !== null) { - _level = level + 1 - } - Vue.set(record, '_level', _level) - // 如果有父元素 - if (parent) { - Vue.set(record, 'parent', parent) - } - tmp.push(record) - if (record.children && record.children.length > 0) { - const children = treeToArray(record.children, expandAll, record, _level) - tmp = tmp.concat(children) - } - }) - return tmp -} diff --git a/frontend/src/components/TreeTable/index.vue b/frontend/src/components/TreeTable/index.vue deleted file mode 100644 index a48765c8..00000000 --- a/frontend/src/components/TreeTable/index.vue +++ /dev/null @@ -1,127 +0,0 @@ - - - - - - - - - - {{ scope.$index }} - - - - - - - - - - - - {{ scope.row[column.value] }} - - - - - - - - - - diff --git a/frontend/src/components/TreeTable/readme.md b/frontend/src/components/TreeTable/readme.md deleted file mode 100644 index 5b598e11..00000000 --- a/frontend/src/components/TreeTable/readme.md +++ /dev/null @@ -1,89 +0,0 @@ -## 写在前面 -此组件仅提供一个创建TreeTable的解决思路 - -## prop说明 -#### *data* - **必填** - - 原始数据,要求是一个数组或者对象 - ```javascript - [{ - key1: value1, - key2: value2, - children: [{ - key1: value1 - }, - { - key1: value1 - }] - }, - { - key1: value1 - }] - ``` - 或者 - ```javascript - { - key1: value1, - key2: value2, - children: [{ - key1: value1 - }, - { - key1: value1 - }] - } - ``` - -#### columns - 列属性,要求是一个数组 - - 1. text: 显示在表头的文字 - 2. value: 对应data的key。treeTable将显示相应的value - 3. width: 每列的宽度,为一个数字(可选) - - 如果你想要每个字段都有自定义的样式或者嵌套其他组件,columns可不提供,直接像在el-table一样写即可,如果没有自定义内容,提供columns将更加的便捷方便 - - 如果你有几个字段是需要自定义的,几个不需要,那么可以将不需要自定义的字段放入columns,将需要自定义的内容放入到slot中,详情见后文 - ```javascript - [{ - value:string, - text:string, - width:number - },{ - value:string, - text:string, - width:number - }] - ``` - -#### expandAll - 是否默认全部展开,boolean值,默认为false - -#### evalFunc - 解析函数,function,非必须 - - 如果不提供,将使用默认的[evalFunc](./eval.js) - - 如果提供了evalFunc,那么会用提供的evalFunc去解析data,并返回treeTable渲染所需要的值。如何编写一个evalFunc,请参考[*eval.js*](https://github.com/PanJiaChen/vue-element-admin/blob/master/src/components/TreeTable/eval.js)或[*customEval.js*](https://github.com/PanJiaChen/vue-element-admin/blob/master/src/views/table/treeTable/customEval.js) - -#### evalArgs - 解析函数的参数,是一个数组 - - **请注意,自定义的解析函数参数第一个为this.data,第二个参数为, this.expandAll,你不需要在evalArgs填写。一定记住,这两个参数是强制性的,并且位置不可颠倒** *this.data为需要解析的数据,this.expandAll为是否默认展开* - - 如你的解析函数需要的参数为`(this.data, this.expandAll,1,2,3,4)`,那么你只需要将`[1,2,3,4]`赋值给`evalArgs`就可以了 - - 如果你的解析函数参数只有`(this.data, this.expandAll)`,那么就可以不用填写evalArgs了 - - 具体可参考[*customEval.js*](https://github.com/PanJiaChen/vue-element-admin/blob/master/src/views/table/treeTable/customEval.js)的函数参数和[customTreeTable](https://github.com/PanJiaChen/vue-element-admin/blob/master/src/views/table/treeTable/customTreeTable.vue)的`evalArgs`属性值 - - ## slot - 这是一个自定义列的插槽。 - - 默认情况下,treeTable只有一行行展示数据的功能。但是一般情况下,我们会要给行加上一个操作按钮或者根据当行数据展示不同的样式,这时我们就需要自定义列了。请参考[customTreeTable](https://github.com/PanJiaChen/vue-element-admin/blob/master/src/views/table/treeTable/customTreeTable.vue),[实例效果](https://panjiachen.github.io/vue-element-admin/#/table/tree-table) - - `slot`和`columns属性`可同时存在,columns里面的数据列会在slot自定义列的左边展示 - - ## 其他 - 如果有其他的需求,请参考[el-table](http://element-cn.eleme.io/#/en-US/component/table)的api自行修改index.vue diff --git a/frontend/src/components/Upload/singleImage.vue b/frontend/src/components/Upload/singleImage.vue deleted file mode 100644 index 291e4cf1..00000000 --- a/frontend/src/components/Upload/singleImage.vue +++ /dev/null @@ -1,132 +0,0 @@ - - - - - 将文件拖到此处,或点击上传 - - - - - - - - - - - - - - - diff --git a/frontend/src/i18n/index.js b/frontend/src/i18n/index.js index e5f6618e..6c89075b 100644 --- a/frontend/src/i18n/index.js +++ b/frontend/src/i18n/index.js @@ -6,7 +6,7 @@ import zh from './zh' Vue.use(VueI18n) const i18n = new VueI18n({ - locale: localStorage.getItem('lang') || 'en', + locale: localStorage.getItem('lang') || 'zh', messages: { en, zh diff --git a/frontend/src/i18n/zh.js b/frontend/src/i18n/zh.js index fb49de32..fc6d545c 100644 --- a/frontend/src/i18n/zh.js +++ b/frontend/src/i18n/zh.js @@ -30,13 +30,13 @@ export default { 'Latest Tasks': '最近任务', 'Latest Deploys': '最近部署', - // 状态 - PENDING: '待定', - STARTED: '已开始', - SUCCESS: '成功', - FAILURE: '错误', - UNAVAILABLE: '未知', - REVOKED: '已取消', + // 任务状态 + Pending: '待定', + Running: '进行中', + Finished: '已完成', + Error: '错误', + NA: '未知', + Cancelled: '已取消', // 操作 Add: '添加', @@ -72,6 +72,7 @@ export default { 'Node Info': '节点信息', 'Node Name': '节点名称', 'Node IP': '节点IP', + 'Node MAC': '节点MAC', 'Node Port': '节点端口', 'Description': '描述', diff --git a/frontend/src/store/modules/node.js b/frontend/src/store/modules/node.js index aec4e681..48c01ee5 100644 --- a/frontend/src/store/modules/node.js +++ b/frontend/src/store/modules/node.js @@ -27,27 +27,11 @@ const actions = { getNodeList ({ state, commit }) { request.get('/nodes', {}) .then(response => { - commit('SET_NODE_LIST', response.data.items) - }) - }, - addNode ({ state, dispatch }) { - request.put('/nodes', { - name: state.nodeForm.name, - ip: state.nodeForm.ip, - port: state.nodeForm.port, - description: state.nodeForm.description - }) - .then(() => { - dispatch('getNodeList') + commit('SET_NODE_LIST', response.data.data) }) }, editNode ({ state, dispatch }) { - request.post(`/nodes/${state.nodeForm._id}`, { - name: state.nodeForm.name, - ip: state.nodeForm.ip, - port: state.nodeForm.port, - description: state.nodeForm.description - }) + request.post(`/nodes/${state.nodeForm._id}`, state.nodeForm) .then(() => { dispatch('getNodeList') }) @@ -61,23 +45,14 @@ const actions = { getNodeData ({ state, commit }, id) { request.get(`/nodes/${id}`) .then(response => { - commit('SET_NODE_FORM', response.data) - }) - }, - getDeployList ({ state, commit }, id) { - return request.get(`/nodes/${id}/get_deploys`) - .then(response => { - commit('deploy/SET_DEPLOY_LIST', - response.data.items.map(d => d) - .sort((a, b) => a.finish_ts < b.finish_ts ? 1 : -1), - { root: true }) + commit('SET_NODE_FORM', response.data.data) }) }, getTaskList ({ state, commit }, id) { - return request.get(`/nodes/${id}/get_tasks`) + return request.get(`/nodes/${id}/tasks`) .then(response => { commit('task/SET_TASK_LIST', - response.data.items.map(d => d) + response.data.data.map(d => d) .sort((a, b) => a.create_ts < b.create_ts ? 1 : -1), { root: true }) }) diff --git a/frontend/src/store/modules/schedule.js b/frontend/src/store/modules/schedule.js index 3c322133..44ab7218 100644 --- a/frontend/src/store/modules/schedule.js +++ b/frontend/src/store/modules/schedule.js @@ -20,7 +20,7 @@ const actions = { getScheduleList ({ state, commit }) { request.get('/schedules') .then(response => { - commit('SET_SCHEDULE_LIST', response.data.items) + commit('SET_SCHEDULE_LIST', response.data.data) }) }, addSchedule ({ state }) { diff --git a/frontend/src/store/modules/spider.js b/frontend/src/store/modules/spider.js index 25f1260f..1d603e87 100644 --- a/frontend/src/store/modules/spider.js +++ b/frontend/src/store/modules/spider.js @@ -76,43 +76,13 @@ const actions = { if (state.filterSite) { params.site = state.filterSite } - console.log(params) return request.get('/spiders', params) .then(response => { - commit('SET_SPIDER_LIST', response.data.items) - }) - }, - addSpider ({ state, dispatch }) { - return request.put('/spiders', { - name: state.spiderForm.name, - col: state.spiderForm.col, - type: 'configurable', - site: state.spiderForm.site - }) - .then(() => { - dispatch('getSpiderList') + commit('SET_SPIDER_LIST', response.data.data) }) }, editSpider ({ state, dispatch }) { - return request.post(`/spiders/${state.spiderForm._id}`, { - name: state.spiderForm.name, - src: state.spiderForm.src, - cmd: state.spiderForm.cmd, - type: state.spiderForm.type, - lang: state.spiderForm.lang, - col: state.spiderForm.col, - site: state.spiderForm.site, - // configurable spider - crawl_type: state.spiderForm.crawl_type, - start_url: state.spiderForm.start_url, - url_pattern: state.spiderForm.url_pattern, - item_selector: state.spiderForm.item_selector, - item_selector_type: state.spiderForm.item_selector_type, - pagination_selector: state.spiderForm.pagination_selector, - pagination_selector_type: state.spiderForm.pagination_selector_type, - obey_robots_txt: state.spiderForm.obey_robots_txt, - item_threshold: state.spiderForm.item_threshold - }) + return request.post(`/spiders/${state.spiderForm._id}`, state.spiderForm) .then(() => { dispatch('getSpiderList') }) @@ -123,60 +93,24 @@ const actions = { dispatch('getSpiderList') }) }, - updateSpiderEnvs ({ state }) { - return request.post(`/spiders/${state.spiderForm._id}/update_envs`, { - envs: JSON.stringify(state.spiderForm.envs) - }) - }, - updateSpiderFields ({ state }) { - return request.post(`/spiders/${state.spiderForm._id}/update_fields`, { - fields: JSON.stringify(state.spiderForm.fields) - }) - }, - updateSpiderDetailFields ({ state }) { - return request.post(`/spiders/${state.spiderForm._id}/update_detail_fields`, { - detail_fields: JSON.stringify(state.spiderForm.detail_fields) - }) - }, getSpiderData ({ state, commit }, id) { return request.get(`/spiders/${id}`) .then(response => { - let data = response.data - data.cron_enabled = !!data.cron_enabled + let data = response.data.data commit('SET_SPIDER_FORM', data) }) }, - deploySpider ({ state, dispatch }, id) { - return request.post(`/spiders/${id}/deploy`) - .then(response => { - console.log(response.data) - }) - .then(response => { - dispatch('getSpiderData', id) - dispatch('getSpiderList') - }) - }, crawlSpider ({ state, dispatch }, id) { - return request.post(`/spiders/${id}/on_crawl`) - .then(response => { - console.log(response.data) - }) - }, - getDeployList ({ state, commit }, id) { - return request.get(`/spiders/${id}/get_deploys`) - .then(response => { - commit('deploy/SET_DEPLOY_LIST', - response.data.items.map(d => { - return d - }).sort((a, b) => a.finish_ts < b.finish_ts ? 1 : -1), - { root: true }) - }) + return request.put(`/tasks`, { + spider_id: id + // TODO: node_id + }) }, getTaskList ({ state, commit }, id) { - return request.get(`/spiders/${id}/get_tasks`) + return request.get(`/spiders/${id}/tasks`) .then(response => { commit('task/SET_TASK_LIST', - response.data.items.map(d => { + response.data.data.map(d => { return d }).sort((a, b) => a.create_ts < b.create_ts ? 1 : -1), { root: true }) @@ -185,15 +119,6 @@ const actions = { importGithub ({ state }) { const url = state.importForm.url return request.post('/spiders/import/github', { url }) - .then(response => { - console.log(response) - }) - }, - deployAll () { - return request.post('/spiders/manage/deploy_all') - .then(response => { - console.log(response) - }) }, getSpiderStats ({ state, commit }) { return request.get('/stats/get_spider_stats?spider_id=' + state.spiderForm._id) diff --git a/frontend/src/store/modules/task.js b/frontend/src/store/modules/task.js index 906f9f69..d0520087 100644 --- a/frontend/src/store/modules/task.js +++ b/frontend/src/store/modules/task.js @@ -15,14 +15,28 @@ const state = { spider_id: '' }, // pagination - pageNum: 0, + pageNum: 1, pageSize: 10, // results resultsPageNum: 1, resultsPageSize: 10 } -const getters = {} +const getters = { + taskResultsColumns (state) { + if (!state.taskResultsData.length) { + return [] + } + const keys = [] + const item = state.taskResultsData[0] + for (const key in item) { + if (item.hasOwnProperty(key)) { + keys.push(key) + } + } + return keys + } +} const mutations = { SET_TASK_FORM (state, value) { @@ -64,8 +78,9 @@ const actions = { getTaskData ({ state, dispatch, commit }, id) { return request.get(`/tasks/${id}`) .then(response => { - let data = response.data + let data = response.data.data commit('SET_TASK_FORM', data) + console.log(data) dispatch('spider/getSpiderData', data.spider_id, { root: true }) dispatch('node/getNodeData', data.node_id, { root: true }) }) @@ -74,14 +89,12 @@ const actions = { return request.get('/tasks', { page_num: state.pageNum, page_size: state.pageSize, - filter: { - node_id: state.filter.node_id || undefined, - spider_id: state.filter.spider_id || undefined - } + node_id: state.filter.node_id || undefined, + spider_id: state.filter.spider_id || undefined }) .then(response => { - commit('SET_TASK_LIST', response.data.items) - commit('SET_TASK_LIST_TOTAL_COUNT', response.data.total_count) + commit('SET_TASK_LIST', response.data.data || []) + commit('SET_TASK_LIST_TOTAL_COUNT', response.data.total) }) }, deleteTask ({ state, dispatch }, id) { @@ -97,21 +110,20 @@ const actions = { }) }, getTaskLog ({ state, commit }, id) { - return request.get(`/tasks/${id}/get_log`) + return request.get(`/tasks/${id}/log`) .then(response => { - commit('SET_TASK_LOG', response.data.log) + commit('SET_TASK_LOG', response.data.data) }) }, getTaskResults ({ state, commit }, id) { - return request.get(`/tasks/${id}/get_results`, { + return request.get(`/tasks/${id}/results`, { page_num: state.resultsPageNum, page_size: state.resultsPageSize }) .then(response => { - commit('SET_TASK_RESULTS_DATA', response.data.items) - commit('SET_TASK_RESULTS_COLUMNS', response.data.fields) - commit('SET_TASK_RESULTS_COLUMNS', response.data.fields) - commit('SET_TASK_RESULTS_TOTAL_COUNT', response.data.total_count) + commit('SET_TASK_RESULTS_DATA', response.data.data) + // commit('SET_TASK_RESULTS_COLUMNS', response.data.fields) + commit('SET_TASK_RESULTS_TOTAL_COUNT', response.data.total) }) } } diff --git a/frontend/src/views/node/NodeDetail.vue b/frontend/src/views/node/NodeDetail.vue index 7de7a677..527da581 100644 --- a/frontend/src/views/node/NodeDetail.vue +++ b/frontend/src/views/node/NodeDetail.vue @@ -57,9 +57,6 @@ export default { // get node basic info this.$store.dispatch('node/getNodeData', this.$route.params.id) - // get node deploy list - this.$store.dispatch('node/getDeployList', this.$route.params.id) - // get node task list this.$store.dispatch('node/getTaskList', this.$route.params.id) } diff --git a/frontend/src/views/node/NodeList.vue b/frontend/src/views/node/NodeList.vue index be916f44..33d8a67f 100644 --- a/frontend/src/views/node/NodeList.vue +++ b/frontend/src/views/node/NodeList.vue @@ -41,7 +41,7 @@ :property="col.name" :label="$t(col.label)" :sortable="col.sortable" - align="center" + :align="col.align || 'left'" :width="col.width"> @@ -50,9 +50,6 @@ - - - @@ -95,7 +92,7 @@ export default { columns: [ { name: 'name', label: 'Name', width: '220' }, { name: 'ip', label: 'IP', width: '160' }, - { name: 'port', label: 'Port', width: '80' }, + // { name: 'port', label: 'Port', width: '80' }, { name: 'status', label: 'Status', width: '120', sortable: true }, { name: 'description', label: 'Description', width: 'auto' } ], diff --git a/frontend/src/views/result/ResultDetail.vue b/frontend/src/views/result/ResultDetail.vue index 339d56b0..d9f8d68f 100644 --- a/frontend/src/views/result/ResultDetail.vue +++ b/frontend/src/views/result/ResultDetail.vue @@ -28,7 +28,7 @@ import FileList from '../../components/FileList/FileList' import SpiderOverview from '../../components/Overview/SpiderOverview' export default { - name: 'NodeDetail', + name: 'ResultDetail', components: { FileList, SpiderOverview diff --git a/frontend/src/views/schedule/ScheduleList.vue b/frontend/src/views/schedule/ScheduleList.vue index 8887e947..e097ff3f 100644 --- a/frontend/src/views/schedule/ScheduleList.vue +++ b/frontend/src/views/schedule/ScheduleList.vue @@ -14,6 +14,18 @@ + + + + + + @@ -90,11 +102,11 @@ :property="col.name" :label="$t(col.label)" :sortable="col.sortable" - align="center" + :align="col.align" :width="col.width"> - + @@ -136,8 +148,10 @@ export default { } return { columns: [ - { name: 'name', label: 'Name', width: '220' }, - { name: 'cron', label: 'Cron', width: '220' }, + { name: 'name', label: 'Name', width: '180' }, + { name: 'cron', label: 'Cron', width: '120' }, + { name: 'node_name', label: 'Node', width: '150' }, + { name: 'spider_name', label: 'Spider', width: '150' }, { name: 'description', label: 'Description', width: 'auto' } ], isEdit: false, @@ -158,6 +172,9 @@ export default { ...mapState('spider', [ 'spiderList' ]), + ...mapState('node', [ + 'nodeList' + ]), filteredTableData () { return this.scheduleList }, @@ -245,6 +262,7 @@ export default { created () { this.$store.dispatch('schedule/getScheduleList') this.$store.dispatch('spider/getSpiderList') + this.$store.dispatch('node/getNodeList') } } diff --git a/frontend/src/views/spider/SpiderDetail.vue b/frontend/src/views/spider/SpiderDetail.vue index 2368e8e7..ce7bfb5d 100644 --- a/frontend/src/views/spider/SpiderDetail.vue +++ b/frontend/src/views/spider/SpiderDetail.vue @@ -40,7 +40,7 @@ import SpiderStats from '../../components/Stats/SpiderStats' import ConfigList from '../../components/Config/ConfigList' export default { - name: 'NodeDetail', + name: 'SpiderDetail', components: { ConfigList, SpiderStats, @@ -96,9 +96,6 @@ export default { this.$store.dispatch('file/getFileList', this.spiderForm.src) }) - // get spider deploys - this.$store.dispatch('spider/getDeployList', this.$route.params.id) - // get spider tasks this.$store.dispatch('spider/getTaskList', this.$route.params.id) } diff --git a/frontend/src/views/spider/SpiderList.vue b/frontend/src/views/spider/SpiderList.vue index 985a0d7a..d5eff01d 100644 --- a/frontend/src/views/spider/SpiderList.vue +++ b/frontend/src/views/spider/SpiderList.vue @@ -111,9 +111,6 @@ - - {{$t('Deploy All')}} - {{$t('Import Spiders')}} @@ -173,6 +170,15 @@ + + + + + - + - - - - - + + - + @@ -244,7 +247,8 @@ export default { { name: 'name', label: 'Name', width: '180', align: 'left' }, { name: 'site_name', label: 'Site', width: '140', align: 'left' }, { name: 'type', label: 'Spider Type', width: '120' }, - { name: 'lang', label: 'Language', width: '120', sortable: true }, + // { name: 'cmd', label: 'Command Line', width: '200' }, + // { name: 'lang', label: 'Language', width: '120', sortable: true }, { name: 'task_ts', label: 'Last Run', width: '160' }, { name: 'last_7d_tasks', label: 'Last 7-Day Tasks', width: '80' }, { name: 'last_5_errors', label: 'Last 5-Run Errors', width: '80' } @@ -371,22 +375,6 @@ export default { this.$st.sendEv('爬虫', '删除') }) }, - onDeploy (row) { - this.$confirm(this.$t('Are you sure to deploy this spider?'), this.$t('Notification'), { - confirmButtonText: this.$t('Confirm'), - cancelButtonText: this.$t('Cancel'), - type: 'warning' - }).then(() => { - this.$store.dispatch('spider/deploySpider', row._id) - .then(() => { - this.$message({ - type: 'success', - message: 'Deployed successfully' - }) - }) - this.$st.sendEv('爬虫', '部署') - }) - }, onCrawl (row) { this.$confirm(this.$t('Are you sure to run this spider?'), this.$t('Notification'), { confirmButtonText: this.$t('Confirm'), @@ -431,26 +419,9 @@ export default { openImportDialog () { this.dialogVisible = true }, - onDeployAll () { - this.$confirm(this.$t('Are you sure to deploy all spiders to active nodes?'), this.$t('Notification'), { - confirmButtonText: this.$t('Confirm'), - cancelButtonText: this.$t('Cancel'), - type: 'warning' - }) - .then(() => { - this.$store.dispatch('spider/deployAll') - .then(() => { - this.$message.success(this.$t('Deployed all spiders successfully')) - }) - this.$st.sendEv('爬虫', '部署所有爬虫') - }) - }, isShowRun (row) { if (this.isCustomized(row)) { // customized spider - if (!row.deploy_ts) { - return false - } return !!row.cmd } else { // configurable spider diff --git a/frontend/src/views/task/TaskDetail.vue b/frontend/src/views/task/TaskDetail.vue index 053ef963..86f1a33a 100644 --- a/frontend/src/views/task/TaskDetail.vue +++ b/frontend/src/views/task/TaskDetail.vue @@ -29,7 +29,8 @@ diff --git a/frontend/src/views/task/TaskList.vue b/frontend/src/views/task/TaskList.vue index 4d514b45..ae50c11a 100644 --- a/frontend/src/views/task/TaskList.vue +++ b/frontend/src/views/task/TaskList.vue @@ -40,17 +40,57 @@ :key="col.name" :label="$t(col.label)" :sortable="col.sortable" - align="center" + :align="col.align" :width="col.width"> {{scope.row[col.name]}} - + + {{getTime(scope.row[col.name])}} + + + + + {{getWaitDuration(scope.row)}} + + + + + {{getRuntimeDuration(scope.row)}} + + + + + {{getTotalDuration(scope.row)}} + + + {{scope.row[col.name]}} @@ -60,13 +100,10 @@ :key="col.name" :label="$t(col.label)" :sortable="col.sortable" - align="center" + :align="col.align" :width="col.width"> - {{$t('SUCCESS')}} - {{$t('STARTED')}} - {{$t('FAILURE')}} - {{$t(scope.row[col.name])}} + - + @@ -107,9 +144,12 @@ import { mapState } from 'vuex' +import dayjs from 'dayjs' +import StatusTag from '../../components/Status/StatusTag' export default { name: 'TaskList', + components: { StatusTag }, data () { return { // setInterval handle @@ -123,15 +163,17 @@ export default { // table columns columns: [ + { name: 'node_name', label: 'Node', width: '120' }, + { name: 'spider_name', label: 'Spider', width: '120' }, + { name: 'status', label: 'Status', width: '120' }, { name: 'create_ts', label: 'Create Time', width: '100' }, { name: 'start_ts', label: 'Start Time', width: '100' }, { name: 'finish_ts', label: 'Finish Time', width: '100' }, - { name: 'duration', label: 'Duration (sec)', width: '80' }, - { name: 'spider_name', label: 'Spider', width: '120' }, - { name: 'node_id', label: 'Node', width: '160' }, - { name: 'num_results', label: 'Results Count', width: '80' }, - { name: 'avg_num_results', label: 'Average Results Count per Second', width: '80' }, - { name: 'status', label: 'Status', width: '80' } + { name: 'wait_duration', label: 'Wait Duration (sec)', width: '80', align: 'right' }, + { name: 'runtime_duration', label: 'Runtime Duration (sec)', width: '80', align: 'right' }, + { name: 'total_duration', label: 'Total Duration (sec)', width: '80', align: 'right' }, + { name: 'num_results', label: 'Results Count', width: '80' } + // { name: 'avg_num_results', label: 'Average Results Count per Second', width: '80' } ] } }, @@ -233,6 +275,22 @@ export default { setTimeout(() => { this.$store.dispatch('task/getTaskList') }, 0) + }, + getTime (str) { + if (str.match('^0001')) return 'NA' + return dayjs(str).format('YYYY-MM-DD HH:mm:ss') + }, + getWaitDuration (row) { + if (row.start_ts.match('^0001')) return 'NA' + return dayjs(row.start_ts).diff(row.create_ts, 'second') + }, + getRuntimeDuration (row) { + if (row.finish_ts.match('^0001')) return 'NA' + return dayjs(row.finish_ts).diff(row.start_ts, 'second') + }, + getTotalDuration (row) { + if (row.finish_ts.match('^0001')) return 'NA' + return dayjs(row.finish_ts).diff(row.create_ts, 'second') } }, created () { diff --git a/spiders/article/article_spider.js b/spiders/article/article_spider.js deleted file mode 100644 index c94f2820..00000000 --- a/spiders/article/article_spider.js +++ /dev/null @@ -1,61 +0,0 @@ -const puppeteer = require('puppeteer'); -const MongoClient = require('mongodb').MongoClient; - -(async () => { - // browser - const browser = await (puppeteer.launch({ - headless: true - })); - - // page - const page = await browser.newPage(); - - // open database connection - const client = await MongoClient.connect('mongodb://127.0.0.1:27017'); - let db = await client.db('crawlab_test'); - const colName = process.env.CRAWLAB_COLLECTION || 'results'; - const col = db.collection(colName); - const col_src = db.collection('results'); - - const results = await col_src.find({content: {$exists: false}}).toArray(); - for (let i = 0; i < results.length; i++) { - let item = results[i]; - - // define article anchor - let anchor; - if (item.source === 'juejin') { - anchor = '.article-content'; - } else if (item.source === 'segmentfault') { - anchor = '.article'; - } else if (item.source === 'csdn') { - anchor = '#content_views'; - } else { - continue; - } - - console.log(`anchor: ${anchor}`); - - // navigate to the article - try { - await page.goto(item.url, {waitUntil: 'domcontentloaded'}); - await page.waitFor(2000); - } catch (e) { - console.error(e); - continue; - } - - // scrape article content - item.content = await page.$eval(anchor, el => el.innerHTML); - - // save to database - await col.save(item); - console.log(`saved item: ${JSON.stringify(item)}`) - } - - // close mongodb - client.close(); - - // close browser - browser.close(); - -})(); \ No newline at end of file diff --git a/spiders/sinastock/sinastock/spiders/sinastock_spider.py b/spiders/sinastock/sinastock/spiders/sinastock_spider.py index 6605a794..9d258e6c 100644 --- a/spiders/sinastock/sinastock/spiders/sinastock_spider.py +++ b/spiders/sinastock/sinastock/spiders/sinastock_spider.py @@ -5,13 +5,9 @@ from datetime import datetime import scrapy from pymongo import MongoClient -import pytz from sinastock.items import NewsItem -# 时区 -tz = pytz.timezone('Asia/Shanghai') - class SinastockSpiderSpider(scrapy.Spider): name = 'sinastock_spider' @@ -22,13 +18,12 @@ class SinastockSpiderSpider(scrapy.Spider): ) db = mongo[os.environ.get('MONGO_DB') or 'crawlab_test'] col = db.get_collection(os.environ.get('CRAWLAB_COLLECTION') or 'stock_news') - page_num = int(os.environ.get('PAGE_NUM')) or 3 def start_requests(self): col = self.db['stocks'] for s in col.find({}): code, ex = s['ts_code'].split('.') - for i in range(self.page_num): + for i in range(10): url = f'http://vip.stock.finance.sina.com.cn/corp/view/vCB_AllNewsStock.php?symbol={ex.lower()}{code}&Page={i + 1}' yield scrapy.Request( url=url, @@ -61,7 +56,5 @@ class SinastockSpiderSpider(scrapy.Spider): if item['text'] is None or item['ts_str'] is None: pass else: - ts = datetime.strptime(item['ts_str'], '%Y年%m月%d日 %H:%M') - ts = tz.localize(ts) - item['ts'] = ts + item['ts'] = datetime.strptime(item['ts_str'], '%Y年%m月%d日 %H:%M') yield item