This commit is contained in:
Yang Luo 2020-09-01 01:04:29 +08:00
Родитель e4e8bc8b8c
Коммит 6cf586ff2a
8 изменённых файлов: 507 добавлений и 0 удалений

57
controllers/captor.go Normal file
Просмотреть файл

@ -0,0 +1,57 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
package controllers
import (
"encoding/json"
"github.com/microsoft/mouselog/object"
)
func (c *ApiController) GetCaptors() {
c.Data["json"] = object.GetCaptors()
c.ServeJSON()
}
func (c *ApiController) GetCaptor() {
id := c.Input().Get("id")
c.Data["json"] = object.GetCaptor(id)
c.ServeJSON()
}
func (c *ApiController) UpdateCaptor() {
id := c.Input().Get("id")
var captor object.Captor
err := json.Unmarshal(c.Ctx.Input.RequestBody, &captor)
if err != nil {
panic(err)
}
c.Data["json"] = object.UpdateCaptor(id, &captor)
c.ServeJSON()
}
func (c *ApiController) AddCaptor() {
var captor object.Captor
err := json.Unmarshal(c.Ctx.Input.RequestBody, &captor)
if err != nil {
panic(err)
}
c.Data["json"] = object.AddCaptor(&captor)
c.ServeJSON()
}
func (c *ApiController) DeleteCaptor() {
var captor object.Captor
err := json.Unmarshal(c.Ctx.Input.RequestBody, &captor)
if err != nil {
panic(err)
}
c.Data["json"] = object.DeleteCaptor(&captor)
c.ServeJSON()
}

68
object/captor.go Normal file
Просмотреть файл

@ -0,0 +1,68 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
package object
type Captor struct {
Name string `xorm:"varchar(100) notnull pk" json:"name"`
CreatedTime string `xorm:"varchar(100)" json:"createdTime"`
Title string `xorm:"varchar(100)" json:"title"`
Script string `xorm:"mediumtext" json:"script"`
}
func GetCaptors() []*Captor {
captors := []*Captor{}
err := ormManager.engine.Desc("created_time").Find(&captors)
if err != nil {
panic(err)
}
return captors
}
func GetCaptor(id string) *Captor {
s := Captor{Name: id}
existed, err := ormManager.engine.Get(&s)
if err != nil {
panic(err)
}
if existed {
return &s
} else {
return nil
}
}
func UpdateCaptor(id string, captor *Captor) bool {
if GetCaptor(id) == nil {
return false
}
_, err := ormManager.engine.Id(id).AllCols().Update(captor)
if err != nil {
panic(err)
}
//return affected != 0
return true
}
func AddCaptor(captor *Captor) bool {
affected, err := ormManager.engine.Insert(captor)
if err != nil {
panic(err)
}
return affected != 0
}
func DeleteCaptor(captor *Captor) bool {
affected, err := ormManager.engine.Id(captor.Name).Delete(&Captor{})
if err != nil {
panic(err)
}
return affected != 0
}

Просмотреть файл

@ -104,4 +104,9 @@ func (a *OrmManager) createTables() {
if err != nil {
panic(err)
}
err = a.engine.Sync2(new(Captor))
if err != nil {
panic(err)
}
}

Просмотреть файл

@ -55,4 +55,10 @@ func init() {
beego.Router("/api/update-faker", &controllers.ApiController{}, "POST:UpdateFaker")
beego.Router("/api/add-faker", &controllers.ApiController{}, "POST:AddFaker")
beego.Router("/api/delete-faker", &controllers.ApiController{}, "POST:DeleteFaker")
beego.Router("/api/get-captors", &controllers.ApiController{}, "GET:GetCaptors")
beego.Router("/api/get-captor", &controllers.ApiController{}, "GET:GetCaptor")
beego.Router("/api/update-captor", &controllers.ApiController{}, "POST:UpdateCaptor")
beego.Router("/api/add-captor", &controllers.ApiController{}, "POST:AddCaptor")
beego.Router("/api/delete-captor", &controllers.ApiController{}, "POST:DeleteCaptor")
}

Просмотреть файл

@ -20,6 +20,8 @@ import ImpressionPage from "./ImpressionPage";
import PagePage from "./PagePage";
import FakerListPage from "./FakerListPage";
import FakerEditPage from "./FakerEditPage";
import CaptorListPage from "./CaptorListPage";
import CaptorEditPage from "./CaptorEditPage";
const {Text} = Typography;
const {Header, Footer} = Layout;
@ -57,6 +59,8 @@ class App extends React.Component {
this.setState({selectedMenuKey: 6});
} else if (path.includes('fakers')) {
this.setState({selectedMenuKey: 7});
} else if (path.includes('captors')) {
this.setState({selectedMenuKey: 8});
} else {
this.setState({selectedMenuKey: 1});
}
@ -142,6 +146,11 @@ class App extends React.Component {
Fakers
</a>
</Menu.Item>
<Menu.Item key="8">
<a href="/captors">
Captors
</a>
</Menu.Item>
<Menu.Item key="100" style={{float: 'right'}}>
<a target="_blank" href="https://github.com/microsoft/mouselog">
<img alt="GitHub stars" src="https://img.shields.io/github/stars/microsoft/mouselog?style=social" />
@ -174,6 +183,8 @@ class App extends React.Component {
<Route path="/rules/" component={RuleListPage}/>
<Route exact path="/fakers/" component={FakerListPage}/>
<Route exact path="/fakers/:fakerName" component={FakerEditPage}/>
<Route exact path="/captors/" component={CaptorListPage}/>
<Route exact path="/captors/:captorName" component={CaptorEditPage}/>
<Route exact path="/websites/" component={WebsiteListPage}/>
<Route exact path="/websites/:websiteId" component={WebsiteEditPage}/>
<Route exact path="/websites/:websiteId/sessions" component={SessionPage}/>

149
web/src/CaptorEditPage.js Normal file
Просмотреть файл

@ -0,0 +1,149 @@
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT license.
*/
import React from "react";
import {Button, Card, Col, Input, Row} from 'antd';
import * as CaptorBackend from "./backend/CaptorBackend";
import * as Setting from "./Setting";
import {Controlled as CodeMirror} from 'react-codemirror2'
import "codemirror/lib/codemirror.css"
require("codemirror/mode/lua/lua");
class CaptorEditPage extends React.Component {
constructor(props) {
super(props);
this.state = {
classes: props,
captorName: props.match.params.captorName,
captor: null,
tasks: [],
resources: [],
};
}
componentWillMount() {
this.getCaptor();
}
getCaptor() {
CaptorBackend.getCaptor(this.state.captorName)
.then((captor) => {
this.setState({
captor: captor,
});
});
}
parseCaptorField(key, value) {
// if ([].includes(key)) {
// value = Setting.myParseInt(value);
// }
return value;
}
updateCaptorField(key, value) {
value = this.parseCaptorField(key, value);
let captor = this.state.captor;
captor[key] = value;
this.setState({
captor: captor,
});
}
renderCaptor() {
return (
<Card size="small" title={
<div>
Edit Captor&nbsp;&nbsp;&nbsp;&nbsp;
<Button type="primary" onClick={this.submitCaptorEdit.bind(this)}>Save</Button>
</div>
} style={{marginLeft: '5px'}} type="inner">
<Row style={{marginTop: '10px'}} >
<Col style={{marginTop: '5px'}} span={2}>
Name:
</Col>
<Col span={22} >
<Input value={this.state.captor.name} onChange={e => {
this.updateCaptorField('name', e.target.value);
}} />
</Col>
</Row>
<Row style={{marginTop: '20px'}} >
<Col style={{marginTop: '5px'}} span={2}>
Title:
</Col>
<Col span={22} >
<Input value={this.state.captor.title} onChange={e => {
this.updateCaptorField('title', e.target.value);
}} />
</Col>
</Row>
<Row style={{marginTop: '20px'}} >
<Col style={{marginTop: '5px'}} span={2}>
Script:
</Col>
<Col span={22} >
<CodeMirror
value={this.state.captor.script}
options={{mode: 'lua', theme: "default"}}
onBeforeChange={(editor, data, value) => {
this.updateCaptorField('script', value);
}}
/>
</Col>
</Row>
</Card>
)
}
submitCaptorEdit() {
let captor = Setting.deepCopy(this.state.captor);
CaptorBackend.updateCaptor(this.state.captorName, captor)
.then((res) => {
if (res) {
Setting.showMessage("success", `Successfully saved`);
this.setState({
captorName: this.state.captor.name,
});
this.props.history.push(`/captors/${this.state.captor.name}`);
} else {
Setting.showMessage("error", `failed to save: server side failure`);
this.updateCaptorField('name', this.state.captorName);
}
})
.catch(error => {
Setting.showMessage("error", `failed to save: ${error}`);
});
}
render() {
return (
<div>
<Row style={{width: "100%"}}>
<Col span={1}>
</Col>
<Col span={22}>
{
this.state.captor !== null ? this.renderCaptor() : null
}
</Col>
<Col span={1}>
</Col>
</Row>
<Row style={{margin: 10}}>
<Col span={2}>
</Col>
<Col span={18}>
<Button type="primary" size="large" onClick={this.submitCaptorEdit.bind(this)}>Save</Button>
</Col>
</Row>
</div>
);
}
}
export default CaptorEditPage;

167
web/src/CaptorListPage.js Normal file
Просмотреть файл

@ -0,0 +1,167 @@
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT license.
*/
import React from "react";
import {Button, Col, Popconfirm, Row, Table} from 'antd';
import moment from "moment";
import * as Setting from "./Setting";
import * as CaptorBackend from "./backend/CaptorBackend";
class CaptorListPage extends React.Component {
constructor(props) {
super(props);
this.state = {
classes: props,
captors: null,
};
}
componentWillMount() {
this.getCaptors();
}
getCaptors() {
CaptorBackend.getCaptors()
.then((res) => {
this.setState({
captors: res,
});
});
}
newCaptor() {
return {
owner: "admin", // this.props.account.username,
name: `captor_${this.state.captors.length}`,
createdTime: moment().format(),
title: `New Captor - ${this.state.captors.length}`,
script: "script code",
}
}
addCaptor() {
const newCaptor = this.newCaptor();
CaptorBackend.addCaptor(newCaptor)
.then((res) => {
Setting.showMessage("success", `Captor added successfully`);
this.setState({
captors: Setting.prependRow(this.state.captors, newCaptor),
});
}
)
.catch(error => {
Setting.showMessage("error", `Captor failed to add: ${error}`);
});
}
deleteCaptor(i) {
CaptorBackend.deleteCaptor(this.state.captors[i])
.then((res) => {
Setting.showMessage("success", `Captor deleted successfully`);
this.setState({
captors: Setting.deleteRow(this.state.captors, i),
});
}
)
.catch(error => {
Setting.showMessage("error", `Captor failed to delete: ${error}`);
});
}
renderTable(captors) {
const columns = [
{
title: 'Name',
dataIndex: 'name',
key: 'name',
width: '120px',
sorter: (a, b) => a.name.localeCompare(b.name),
render: (text, record, index) => {
return (
<a href={`/captors/${text}`}>{text}</a>
)
}
},
{
title: 'Title',
dataIndex: 'title',
key: 'title',
// width: '80px',
sorter: (a, b) => a.title.localeCompare(b.title),
},
{
title: 'Created Time',
dataIndex: 'createdTime',
key: 'createdTime',
width: '160px',
sorter: (a, b) => a.createdTime.localeCompare(b.createdTime),
render: (text, record, index) => {
return Setting.getFormattedDate(text);
}
},
{
title: 'Script',
dataIndex: 'script',
key: 'script',
width: '120px',
ellipsis: true,
sorter: (a, b) => a.script.localeCompare(b.script),
},
{
title: 'Action',
dataIndex: '',
key: 'op',
width: '160px',
render: (text, record, index) => {
return (
<div>
<Button style={{marginTop: '10px', marginBottom: '10px', marginRight: '10px'}} type="primary" onClick={() => Setting.goToLink(`/captors/${record.name}`)}>Edit</Button>
<Popconfirm
title={`Sure to delete captor: ${record.name} ?`}
onConfirm={() => this.deleteCaptor(index)}
>
<Button style={{marginBottom: '10px'}} type="danger">Delete</Button>
</Popconfirm>
</div>
)
}
},
];
return (
<div>
<Table columns={columns} dataSource={captors} rowKey="name" size="middle" bordered pagination={{pageSize: 100}}
title={() => (
<div>
Captors&nbsp;&nbsp;&nbsp;&nbsp;
<Button type="primary" size="small" onClick={this.addCaptor.bind(this)}>Add</Button>
</div>
)}
loading={captors === null}
/>
</div>
);
}
render() {
return (
<div>
<Row style={{width: "100%"}}>
<Col span={1}>
</Col>
<Col span={22}>
{
this.renderTable(this.state.captors)
}
</Col>
<Col span={1}>
</Col>
</Row>
</div>
);
}
}
export default CaptorListPage;

Просмотреть файл

@ -0,0 +1,44 @@
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT license.
*/
import * as Setting from "../Setting";
export function getCaptors() {
return fetch(`${Setting.ServerUrl}/api/get-captors`, {
method: "GET",
credentials: "include"
}).then(res => res.json());
}
export function getCaptor(id) {
return fetch(`${Setting.ServerUrl}/api/get-captor?id=${id}`, {
method: "GET",
credentials: "include"
}).then(res => res.json());
}
export function updateCaptor(id, captor) {
return fetch(`${Setting.ServerUrl}/api/update-captor?id=${id}`, {
method: 'POST',
credentials: 'include',
body: JSON.stringify(captor),
}).then(res => res.json());
}
export function addCaptor(captor) {
return fetch(`${Setting.ServerUrl}/api/add-captor`, {
method: 'POST',
credentials: 'include',
body: JSON.stringify(captor),
}).then(res => res.json());
}
export function deleteCaptor(captor) {
return fetch(`${Setting.ServerUrl}/api/delete-captor`, {
method: 'POST',
credentials: 'include',
body: JSON.stringify(captor),
}).then(res => res.json());
}