forked from mirrors/Scribe.js
Updating UI for perf; core fixes
Adding socket options Core changes; config management; serializing
This commit is contained in:
parent
4ac1a16ce4
commit
b29dcb6a6c
95 changed files with 107521 additions and 1520 deletions
32
.babelrc
32
.babelrc
|
@ -1,10 +1,28 @@
|
|||
{
|
||||
"presets": [
|
||||
"es2015",
|
||||
"react"
|
||||
"presets": [
|
||||
"es2015",
|
||||
"react"
|
||||
],
|
||||
"plugins": [
|
||||
"transform-object-rest-spread",
|
||||
"transform-runtime",
|
||||
"syntax-class-properties",
|
||||
"transform-export-default-name",
|
||||
[
|
||||
"transform-async-functions",
|
||||
{
|
||||
"module": "bluebird",
|
||||
"method": "coroutine"
|
||||
}
|
||||
],
|
||||
"plugins": [
|
||||
"transform-object-rest-spread",
|
||||
"transform-runtime"
|
||||
]
|
||||
[
|
||||
"transform-async-to-module-method",
|
||||
{
|
||||
"module": "bluebird",
|
||||
"method": "coroutine"
|
||||
}
|
||||
],
|
||||
"transform-class-properties",
|
||||
"syntax-async-functions"
|
||||
]
|
||||
}
|
141
.scriberc
Normal file
141
.scriberc
Normal file
|
@ -0,0 +1,141 @@
|
|||
{
|
||||
"app": "Scribe",
|
||||
"id": "instance-0",
|
||||
"handleUncaughtException": true,
|
||||
"reader": "reader/BasicConsole",
|
||||
"expose": {
|
||||
"default": [
|
||||
"mongo-socket",
|
||||
"bash"
|
||||
],
|
||||
"express": [
|
||||
"express-mongo-socket",
|
||||
"express-bash"
|
||||
]
|
||||
},
|
||||
"expose/pipeline": {
|
||||
"mongo-socket": [
|
||||
"transform/ErrorExtractor",
|
||||
"transform/ToJSON2",
|
||||
"transform/FullTextSerialize",
|
||||
"writer/MongoDB",
|
||||
"writer/SocketIO"
|
||||
],
|
||||
"express-mongo-socket": [
|
||||
"transform/ExpressExtractor",
|
||||
"transform/ErrorExtractor",
|
||||
"transform/ToJSON2",
|
||||
"transform/FullTextSerialize",
|
||||
"writer/MongoDB",
|
||||
"writer/SocketIO"
|
||||
],
|
||||
"bash": [
|
||||
"transform/Inspector",
|
||||
"writer/DefaultConsole"
|
||||
],
|
||||
"express-bash": [
|
||||
"transform/ExpressExtractor",
|
||||
"transform/ExpressInspector",
|
||||
"transform/Inspector",
|
||||
"writer/DefaultConsole"
|
||||
]
|
||||
},
|
||||
"module": {
|
||||
"middleware/ExpressLogger": {
|
||||
"expose": "express",
|
||||
"ignore": ["(scribe)"]
|
||||
},
|
||||
"writer/MongoDB": {
|
||||
"uri": "mongodb://localhost/scribe"
|
||||
},
|
||||
"writer/SocketIO": {
|
||||
"port": 4000,
|
||||
"options": {}
|
||||
},
|
||||
"transform/Inspector": {
|
||||
"colors": true,
|
||||
"showHidden": false,
|
||||
"depth": 5,
|
||||
"pre": true,
|
||||
"callsite": true,
|
||||
"tags": true,
|
||||
"args": true,
|
||||
"metrics": true
|
||||
},
|
||||
"router/Viewer": {
|
||||
"mongoUri": "mongodb://localhost/scribe",
|
||||
"basePath": "/scribe",
|
||||
"username": "build",
|
||||
"password": "build",
|
||||
"authentication": true,
|
||||
"sessionSecret": "scribe-session",
|
||||
"useBodyParser": true,
|
||||
"useSession": true
|
||||
},
|
||||
"router/Viewer/native": {
|
||||
"protocol": "",
|
||||
"host": "",
|
||||
"port": "",
|
||||
"pathname": ""
|
||||
},
|
||||
"router/Viewer/client": {
|
||||
"background": "#222",
|
||||
"socketPorts": [
|
||||
4000
|
||||
],
|
||||
"socketOptions": {},
|
||||
"queries": {
|
||||
"all": {
|
||||
"label": "all",
|
||||
"query": {
|
||||
"expose": {
|
||||
"$exists": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"error": {
|
||||
"label": "error",
|
||||
"query": {
|
||||
"expose": "error"
|
||||
},
|
||||
"color": "red"
|
||||
},
|
||||
"express": {
|
||||
"label": "express",
|
||||
"query": {
|
||||
"expose": "express"
|
||||
},
|
||||
"color": "blue"
|
||||
},
|
||||
"info": {
|
||||
"label": "info",
|
||||
"query": {
|
||||
"expose": "info"
|
||||
},
|
||||
"color": "cyan"
|
||||
},
|
||||
"log": {
|
||||
"label": "log",
|
||||
"query": {
|
||||
"expose": "log"
|
||||
},
|
||||
"color": "green"
|
||||
},
|
||||
"warn": {
|
||||
"label": "warn",
|
||||
"query": {
|
||||
"expose": "warn"
|
||||
},
|
||||
"color": "yellow"
|
||||
},
|
||||
"trace": {
|
||||
"label": "trace",
|
||||
"query": {
|
||||
"expose": "trace"
|
||||
},
|
||||
"color": "magenta"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -104,7 +104,7 @@ module.exports = function (grunt) {
|
|||
map: false,
|
||||
processors: [
|
||||
require('autoprefixer')({
|
||||
browsers: ['Chrome > 20']
|
||||
browsers: ['> 1%']
|
||||
})
|
||||
]
|
||||
},
|
||||
|
@ -178,7 +178,7 @@ module.exports = function (grunt) {
|
|||
grunt.registerTask('styles', ['sass:dist', 'postcss:dist']);
|
||||
grunt.registerTask('build', ['get-deps', 'sass:dist', 'postcss:dist', 'browserify:dist', 'copy', 'imagemin', 'babel']);
|
||||
grunt.registerTask('default', 'build');
|
||||
grunt.registerTask('auto-build-scripts', ['browserify:dev']);
|
||||
grunt.registerTask('auto-build-styles', ['sass:dev', 'watch:sass']);
|
||||
grunt.registerTask('watch-scripts', ['browserify:dev']);
|
||||
grunt.registerTask('watch-styles', ['sass:dev', 'watch:sass']);
|
||||
grunt.registerTask('production', ['env-force-production', 'clean', 'build', 'uglify:dist', 'env-restore']);
|
||||
};
|
1
Procfile
Normal file
1
Procfile
Normal file
|
@ -0,0 +1 @@
|
|||
web: node ./herokuapp/app.js
|
|
@ -3,7 +3,7 @@
|
|||
Node.js logging made simple! Online access to logs and more...
|
||||
|
||||
```bash
|
||||
npm install git://github.com/bluejamesbond/Scribe.js.git#dev
|
||||
npm install scribe@3.0.0-alpha.5
|
||||
```
|
||||
|
||||
### Features
|
||||
|
@ -16,19 +16,21 @@ npm install git://github.com/bluejamesbond/Scribe.js.git#dev
|
|||
- Support for multithreading (clusters)
|
||||
- Support for logging custom metrics i.e. `databaseResponseTime`
|
||||
- ES6 and Promise support - see examples
|
||||
- Aggregated timeseries of the log entries
|
||||
- Range selection
|
||||
- Keyword searching
|
||||
|
||||
### Future
|
||||
- Add client-side options i.e. hide/display tags, show timings, abstract away search
|
||||
- Endpoint response time graphing
|
||||
- Graphing tools for custom metrics
|
||||
- Support for third-party plugins i.e. data parsing and performance tracking (in progress)
|
||||
- Delete logs from front-end
|
||||
|
||||
## Console
|
||||

|
||||
|
||||
## Web
|
||||

|
||||

|
||||
|
||||
## Native
|
||||

|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
import App from './components/App.jsx'
|
||||
import React from 'react'
|
||||
import MongoDB from './libs/MongoDB'
|
||||
import { render } from 'react-dom'
|
||||
import {render} from 'react-dom'
|
||||
|
||||
window.Meteor = {Collection: {ObjectID: Function}};
|
||||
|
||||
const db = new MongoDB();
|
||||
|
||||
db.init()
|
||||
.then(db=>window.db = db)
|
||||
.then(()=>db.addCollection('Entry'))
|
||||
.then(()=>render(<App />, document.getElementById('app')));
|
||||
.then(db=>window.db = db)
|
||||
.then(()=>db.addCollection('Entry'))
|
||||
.then(()=>render(<App />, document.getElementById('app')));
|
|
@ -8,6 +8,43 @@ import Sidebar from './views/Sidebar.jsx'
|
|||
import ReactList from 'react-list';
|
||||
import querystring from 'querystring'
|
||||
|
||||
class ReactListTracked extends ReactList {
|
||||
constructor(...args) {
|
||||
super(...args);
|
||||
|
||||
this._triggerTimeSeriesBound = this._triggerTimeSeries.bind(this);
|
||||
this.session = null;
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
super.componentDidUpdate();
|
||||
|
||||
this._triggerTimeSeries();
|
||||
}
|
||||
|
||||
_triggerTimeSeries() {
|
||||
const [first] = this.getVisibleRange();
|
||||
|
||||
if (this.props.entries[first]) {
|
||||
Dispatcher.emit(Dispatcher.Events.REQUEST_TIMESERIES_DATE, this.props.entries[first].date);
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
super.componentWillUnmount();
|
||||
|
||||
this.scrollParent.removeEventListener("scroll", this._triggerTimeSeriesBound);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
super.componentDidMount();
|
||||
|
||||
this.scrollParent.addEventListener("scroll", this._triggerTimeSeriesBound);
|
||||
|
||||
window.document.body.style.background = config.background;
|
||||
}
|
||||
}
|
||||
|
||||
class App extends Influx.Component {
|
||||
constructor(...args) {
|
||||
super(...args);
|
||||
|
@ -24,6 +61,12 @@ class App extends Influx.Component {
|
|||
}
|
||||
|
||||
_onEntryStoreUpdated() {
|
||||
const session = EntryStore.getSessionId();
|
||||
if (this.session !== session) {
|
||||
this.lastLength = null;
|
||||
this.session = session;
|
||||
}
|
||||
|
||||
this.setState({entries: EntryStore.getSearchResults()});
|
||||
}
|
||||
|
||||
|
@ -31,7 +74,13 @@ class App extends Influx.Component {
|
|||
Dispatcher.emit(Dispatcher.Events.REQUEST_INIT_SOCKET);
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
const {entries} = this.state;
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
super.componentDidMount();
|
||||
|
||||
Dispatcher.emit(Dispatcher.Events.REQUEST_INIT_DATABASE);
|
||||
}
|
||||
|
||||
|
@ -41,25 +90,33 @@ class App extends Influx.Component {
|
|||
const maxLineChars = String(length).length + 3;
|
||||
|
||||
const rowRenderer = (i, key) => {
|
||||
return <Entry key={key} lines={length} line={length - i} maxLineChars={maxLineChars}
|
||||
entry={this.state.entries[i]}/>
|
||||
const entry = this.state.entries[i];
|
||||
if (i + 1 === length && this.lastLength !== length) {
|
||||
Dispatcher.emit(Dispatcher.Events.REQUEST_GROW_SEARCH);
|
||||
Dispatcher.emit(Dispatcher.Events.REQUEST_TIMESERIES_DATE, entry.date);
|
||||
|
||||
this.lastLength = length;
|
||||
}
|
||||
|
||||
return <Entry key={key} lines={length} line={i + 1} maxLineChars={maxLineChars}
|
||||
entry={entry}/>
|
||||
};
|
||||
|
||||
|
||||
return (
|
||||
<div className="full flex">
|
||||
<Sidebar hide={this.state.hideSidebar}/>
|
||||
<div className='full box'>
|
||||
<div className='full-abs'>
|
||||
<div className="full flex vertical" style={{overflow:'hidden'}}>
|
||||
<Header hide={this.state.hideHeader}/>
|
||||
<div style={{overflow:'scroll',overflowX:'hidden',paddingTop:10}}>
|
||||
<ReactList useTranslate3d={true} length={length} itemRenderer={rowRenderer}/>
|
||||
</div>
|
||||
<div className="full flex">
|
||||
<Sidebar hide={this.state.hideSidebar}/>
|
||||
<div className='full box'>
|
||||
<div className='full-abs'>
|
||||
<div className="full flex vertical" style={{overflow:'hidden'}}>
|
||||
<Header hide={this.state.hideHeader}/>
|
||||
<div style={{overflow:'scroll',overflowX:'hidden',paddingTop:10}}>
|
||||
<ReactListTracked entries={entries} ref='list' useTranslate3d={true} length={length}
|
||||
itemRenderer={rowRenderer}/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
58
asr/viewer/components/views/Checkbox.jsx
Normal file
58
asr/viewer/components/views/Checkbox.jsx
Normal file
|
@ -0,0 +1,58 @@
|
|||
import React from 'react';
|
||||
import {ifcat} from '../../libs/utils';
|
||||
|
||||
class Checkbox extends React.Component {
|
||||
static propTypes = {
|
||||
defaultChecked: React.PropTypes.bool,
|
||||
checked: React.PropTypes.bool,
|
||||
toggle: React.PropTypes.bool,
|
||||
style: React.PropTypes.object,
|
||||
true: React.PropTypes.string,
|
||||
false: React.PropTypes.string,
|
||||
onChange: React.PropTypes.func
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
defaultChecked: false,
|
||||
checked: false,
|
||||
style: {},
|
||||
true: 'on',
|
||||
false: 'off',
|
||||
toggle: false,
|
||||
onChange: () => 0
|
||||
};
|
||||
|
||||
constructor(...args) {
|
||||
super(...args);
|
||||
|
||||
this.state = {checked: this.props.defaultChecked || this.props.checked};
|
||||
}
|
||||
|
||||
componentWillReceiveProps(props) {
|
||||
if (props.checked !== this.state.checked) {
|
||||
this.setState({checked: props.checked});
|
||||
}
|
||||
}
|
||||
|
||||
handleChecked(e) {
|
||||
if (this.props.onChange) {
|
||||
e.target.checked = !this.state.checked;
|
||||
this.props.onChange(e);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div
|
||||
className={ifcat('checkbox', {toggle: this.props.toggle, checked: this.state.checked})}
|
||||
style={this.props.style}
|
||||
onClick={e => this.handleChecked(e)}>
|
||||
{this.props.toggle ? <div
|
||||
className='label'>{this.state.checked ? this.props.true : this.props.false}</div> : null }
|
||||
<div className='check'></div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default Checkbox;
|
|
@ -1,29 +1,51 @@
|
|||
import React from 'react'
|
||||
import Influx from 'react-influx'
|
||||
import {ifcat} from '../../libs/utils'
|
||||
import EntryStore from '../../stores/EntryStore';
|
||||
import Dispatcher from '../../dispatchers/Dispatcher';
|
||||
import ObjectInspector from 'react-object-inspector';
|
||||
import _ from 'underscore'
|
||||
import Checkbox from './Checkbox.jsx';
|
||||
|
||||
export default class Entry extends React.Component {
|
||||
export default class Entry extends Influx.Component {
|
||||
constructor(...args) {
|
||||
super(...args);
|
||||
this.state = {visible: false, width: window.innerWidth};
|
||||
this.state = {visible: false, width: window.innerWidth, selected: EntryStore.isSelected(this.props.entry._id)};
|
||||
this._onWindowResize = this._onWindowResize.bind(this);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
window.removeEventListener("resize", this._onWindowResize)
|
||||
super.componentWillUnmount();
|
||||
|
||||
window.removeEventListener("resize", this._onWindowResize);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
super.componentDidMount();
|
||||
|
||||
window.addEventListener("resize", this._onWindowResize, false)
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
this.setState({selected: EntryStore.isSelected(nextProps.entry._id)});
|
||||
}
|
||||
|
||||
_onWindowResize() {
|
||||
this.setState({width: window.innerWidth});
|
||||
}
|
||||
|
||||
getListeners() {
|
||||
return [
|
||||
[EntryStore, EntryStore.Events.UPDATED_SELECTED, this._onUpdateSelected]
|
||||
]
|
||||
}
|
||||
|
||||
_onUpdateSelected() {
|
||||
this.setState({selected: EntryStore.isSelected(this.props.entry._id)});
|
||||
}
|
||||
|
||||
_inspectPre(persistent) {
|
||||
return <span className="pre yellow">{`${persistent.app}-${persistent.id}`}</span>;
|
||||
return <span className="pre">{`${persistent.app}-${persistent.id}`}</span>;
|
||||
}
|
||||
|
||||
_inspectTags(tags, persistent) {
|
||||
|
@ -35,7 +57,7 @@ export default class Entry extends React.Component {
|
|||
_inspectExpose(expose) {
|
||||
return <span className={ifcat('tag',{
|
||||
fill: true,
|
||||
bgRed:expose == 'error',
|
||||
bgRed: expose == 'error',
|
||||
//bgBlue: expose == 'express',
|
||||
//bgGreen: expose == 'log'
|
||||
})}>{expose}</span>;
|
||||
|
@ -47,11 +69,11 @@ export default class Entry extends React.Component {
|
|||
|
||||
_inspectMetrics(entry) {
|
||||
return (
|
||||
<span>
|
||||
<span>
|
||||
{_.map(entry.transient.metrics, (value, key)=> {
|
||||
value = typeof value === 'number' ? value.toFixed(3) : value;
|
||||
return (
|
||||
<span key={key}>
|
||||
<span key={key}>
|
||||
<span className='tag fill bgPurple'>{key}</span>
|
||||
<span className='tag green'>{value}</span>
|
||||
</span>
|
||||
|
@ -68,12 +90,12 @@ export default class Entry extends React.Component {
|
|||
|
||||
const {entry} = this.props;
|
||||
return (
|
||||
<div className="raw inspector flex">
|
||||
<span className="line" dangerouslySetInnerHTML={{__html:preSpace}}/>
|
||||
<div className="scroll scroll-x">
|
||||
<ObjectInspector data={ entry } initialExpandedPaths={["*"]}/>
|
||||
</div>
|
||||
<div className="raw inspector flex">
|
||||
<span className="line" dangerouslySetInnerHTML={{__html:preSpace}}/>
|
||||
<div className="scroll scroll-x">
|
||||
<ObjectInspector data={ entry } initialExpandedPaths={["*"]}/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -83,7 +105,7 @@ export default class Entry extends React.Component {
|
|||
if (entry.expose === 'express') {
|
||||
const express = entry.args[0];
|
||||
args = (
|
||||
<span>
|
||||
<span>
|
||||
<span className='tag fill bgGreen'>{express.method}</span>
|
||||
<span className='gray'>{express.originalUrl}</span>
|
||||
<span className="green" style={{margin:'0 4px'}}>{express.status} - {express.contentLength} </span>
|
||||
|
@ -104,13 +126,21 @@ export default class Entry extends React.Component {
|
|||
|
||||
_inspectCallSite(entry) {
|
||||
let site = entry.transient.callsite;
|
||||
let tooltip;
|
||||
if (entry.expose === 'express') {
|
||||
const express = entry.args[0];
|
||||
site = `${express.ip}`
|
||||
site = `${express.ip}`;
|
||||
tooltip = 'remote ip';
|
||||
} else {
|
||||
site = `${site.file.substr(site.file.lastIndexOf('/') + 1)}:${site.line}`;
|
||||
tooltip = site.func || 'anonymous';
|
||||
}
|
||||
return <span className={ifcat('tag',{fill: true})}>{site}</span>;
|
||||
return (
|
||||
<span className={ifcat('tag tooltip',{fill: true})}>
|
||||
{site}
|
||||
<span className='tooltiptext tooltip-top'>{tooltip}</span>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
|
@ -118,47 +148,55 @@ export default class Entry extends React.Component {
|
|||
|
||||
const lineNumber = new Array(this.props.maxLineChars - String(this.props.line).length).join(' ') + this.props.line;
|
||||
const preSpace = new Array(this.props.maxLineChars).join(' ');
|
||||
const isPushed = entry._pushed;
|
||||
|
||||
if (this.state.width < 1280) {
|
||||
return (
|
||||
<div className="entry">
|
||||
<div className="pretty flex">
|
||||
<span className="line" dangerouslySetInnerHTML={{__html:lineNumber}}/>
|
||||
<span>{this._inspectPre(entry.persistent)}</span>
|
||||
<span>{this._inspectTags(entry.persistent.tags, true)}</span>
|
||||
<span>{this._inspectTags(entry.transient.tags)}</span>
|
||||
<span>{this._inspectMetrics(entry)}</span>
|
||||
<span className="box"/>
|
||||
<span>{this._inspectExpose(entry.expose)}</span>
|
||||
<span>{this._inspectCallSite(entry)}</span>
|
||||
<span className={ifcat("expand-right gray", {rotate:this.state.visible})}
|
||||
onClick={this._handleClick.bind(this)}>◀</span>
|
||||
</div>
|
||||
<div className="flex">
|
||||
<span className="line" dangerouslySetInnerHTML={{__html:preSpace}}/>
|
||||
<span className="white medium inspector box scroll scroll-x">{this._inspectArgs(entry)}</span>
|
||||
</div>
|
||||
{this._inspectRaw(preSpace) }
|
||||
<div className="entry">
|
||||
<div className="pretty flex">
|
||||
<span className="line" dangerouslySetInnerHTML={{__html:lineNumber}}/>
|
||||
<Checkbox type='checkbox' checked={this.state.selected} style={{marginBottom: 4}}
|
||||
onChange={e => Dispatcher.emit(Dispatcher.Events.REQUEST_SELECT_ENTRY, entry._id, e.target.checked)}/>
|
||||
<span>{this._inspectPre(entry.persistent)}</span>
|
||||
<span>{this._inspectTags(entry.persistent.tags, true)}</span>
|
||||
<span>{this._inspectTags(entry.transient.tags, false, isPushed)}</span>
|
||||
<span>{this._inspectMetrics(entry)}</span>
|
||||
<span className="box"/>
|
||||
<span>{this._inspectExpose(entry.expose)}</span>
|
||||
<span>{this._inspectCallSite(entry)}</span>
|
||||
{ isPushed ? <span><span className='tag fill new-entry'>new</span></span> : null }
|
||||
<span className={ifcat("expand-right gray", {rotate:this.state.visible})}
|
||||
onClick={this._handleClick.bind(this)}>◀</span>
|
||||
</div>
|
||||
<div className="flex">
|
||||
<span className="line" dangerouslySetInnerHTML={{__html:preSpace}}/>
|
||||
<span className="white medium inspector box scroll scroll-x"
|
||||
style={{marginLeft: 22}}>{this._inspectArgs(entry)}</span>
|
||||
</div>
|
||||
{ this._inspectRaw(preSpace) }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="entry">
|
||||
<div className="flex">
|
||||
<span className="line" dangerouslySetInnerHTML={{__html:lineNumber}}/>
|
||||
<span>{this._inspectPre(entry.persistent)}</span>
|
||||
<span>{this._inspectTags(entry.persistent.tags, true)}</span>
|
||||
<span>{this._inspectTags(entry.transient.tags)}</span>
|
||||
<span>{this._inspectMetrics(entry)}</span>
|
||||
<span className="white medium inspector box scroll scroll-x">{this._inspectArgs(entry)}</span>
|
||||
<span>{this._inspectExpose(entry.expose)}</span>
|
||||
<span>{this._inspectCallSite(entry)}</span>
|
||||
<span className={ifcat("expand-right gray", {rotate:this.state.visible})}
|
||||
onClick={this._handleClick.bind(this)}>◀</span>
|
||||
</div>
|
||||
{this._inspectRaw(preSpace) }
|
||||
<div className="entry">
|
||||
<div className="flex">
|
||||
<span className="line" dangerouslySetInnerHTML={{__html:lineNumber}}/>
|
||||
<Checkbox type='checkbox' checked={this.state.selected}
|
||||
onChange={e => Dispatcher.emit(Dispatcher.Events.REQUEST_SELECT_ENTRY, entry._id, e.target.checked)}/>
|
||||
<span>{this._inspectPre(entry.persistent)}</span>
|
||||
<span>{this._inspectTags(entry.persistent.tags, true)}</span>
|
||||
<span>{this._inspectTags(entry.transient.tags, false, isPushed)}</span>
|
||||
<span>{this._inspectMetrics(entry)}</span>
|
||||
<span className="white medium inspector box scroll scroll-x">{this._inspectArgs(entry)}</span>
|
||||
<span>{this._inspectExpose(entry.expose)}</span>
|
||||
<span>{this._inspectCallSite(entry)}</span>
|
||||
{ isPushed ? <span><span className='tag fill new-entry'>new</span></span> : null }
|
||||
<span className={ifcat("expand-right gray", {rotate:this.state.visible})}
|
||||
onClick={this._handleClick.bind(this)}>◀</span>
|
||||
</div>
|
||||
{this._inspectRaw(preSpace) }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -4,6 +4,10 @@ import {ifcat} from '../../libs/utils'
|
|||
import EntryStore from '../../stores/EntryStore'
|
||||
import Dispatcher from '../../dispatchers/Dispatcher'
|
||||
import Spinner from 'react-spinkit'
|
||||
import _ from 'underscore';
|
||||
import moment from 'moment';
|
||||
import TimeSeries from './TimeSeries.jsx';
|
||||
import DatePicker from 'react-datepicker'
|
||||
|
||||
import AceEditor from 'react-ace';
|
||||
import brace from 'brace'
|
||||
|
@ -13,22 +17,54 @@ import 'brace/theme/monokai';
|
|||
export default class Header extends Influx.Component {
|
||||
constructor(...args) {
|
||||
super(...args);
|
||||
this.state = {search: {}, searchEnabled: false};
|
||||
this.state = {
|
||||
search: {
|
||||
period: {}
|
||||
},
|
||||
selected: [],
|
||||
searchEnabled: false,
|
||||
startDate: moment(),
|
||||
endDate: moment()
|
||||
};
|
||||
|
||||
this.search = {};
|
||||
this.text = null;
|
||||
this.expanded = true;
|
||||
this.isTyping = false;
|
||||
}
|
||||
|
||||
getListeners() {
|
||||
return [
|
||||
[EntryStore, EntryStore.Events.UPDATED, this._onEntryStoreUpdated]
|
||||
[EntryStore, EntryStore.Events.UPDATED, this._onEntryStoreUpdated],
|
||||
[EntryStore, EntryStore.Events.UPDATED_SELECTED, this._onEntryStoreUpdatedSelected]
|
||||
]
|
||||
}
|
||||
|
||||
_onEntryStoreUpdated() {
|
||||
const search = {query: EntryStore.getSearchQuery(), options: EntryStore.getSearchOptions()};
|
||||
this.setState({search, searchEnabled: true});
|
||||
const search = {
|
||||
query: EntryStore.getSearchQuery(),
|
||||
options: EntryStore.getSearchOptions(),
|
||||
period: EntryStore.getSearchPeriod(),
|
||||
text: EntryStore.getSessionText()
|
||||
};
|
||||
|
||||
if (!this.isTyping) {
|
||||
this.refs.input.value = search.text || '';
|
||||
}
|
||||
|
||||
const startDate = moment(search.period.$gt);
|
||||
const endDate = moment(search.period.$lt).subtract(1, 'days');
|
||||
|
||||
this.setState({search, searchEnabled: true, startDate, endDate});
|
||||
this.search = JSON.stringify(search);
|
||||
}
|
||||
|
||||
_onEntryStoreUpdatedSelected() {
|
||||
const selected = EntryStore.getSelected();
|
||||
this.setState({selected});
|
||||
this.selected = JSON.stringify(selected);
|
||||
}
|
||||
|
||||
_handleChange(key, data) {
|
||||
if (!this.state.searchEnabled) {
|
||||
return;
|
||||
|
@ -44,16 +80,148 @@ export default class Header extends Influx.Component {
|
|||
_handleSearch() {
|
||||
try {
|
||||
const {query,options} = this.state.search;
|
||||
Dispatcher.emit(Dispatcher.Events.REQUEST_ENTRY_SEARCH, query, options, true);
|
||||
|
||||
Dispatcher.emit(Dispatcher.Events.REQUEST_ENTRY_SEARCH, query, options);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
_triggerTextSearch() {
|
||||
try {
|
||||
const {query, options, period} = this.state.search;
|
||||
Dispatcher.emit(Dispatcher.Events.REQUEST_ENTRY_SEARCH, query, options, period, this.text);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
_handleTextSearch(text) {
|
||||
clearTimeout(this.triggerTid);
|
||||
this.text = text;
|
||||
this.isTyping = true;
|
||||
this.triggerTid = setTimeout(() => this._triggerTextSearch(), 400);
|
||||
}
|
||||
|
||||
_handleDateChange({ startDate, endDate }) {
|
||||
startDate = startDate || this.state.startDate;
|
||||
endDate = endDate || this.state.endDate;
|
||||
|
||||
if (startDate.isAfter(endDate)) {
|
||||
var temp = startDate;
|
||||
startDate = endDate;
|
||||
endDate = temp
|
||||
}
|
||||
|
||||
// if (moment.duration(endDate.diff(startDate)).asDays() > 5) {
|
||||
// startDate = moment(endDate).subtract(5, 'days');
|
||||
// }
|
||||
|
||||
Dispatcher.emit(Dispatcher.Events.REQUEST_PERIOD, {
|
||||
$lt: endDate.add(1, 'days').startOf('day').toISOString(),
|
||||
$gt: startDate.startOf('day').toISOString()
|
||||
});
|
||||
|
||||
this.setState({startDate, endDate})
|
||||
}
|
||||
|
||||
_handleChangeStartDate(startDate) {
|
||||
this._handleDateChange({startDate})
|
||||
}
|
||||
|
||||
_handleChangeEndDate(endDate) {
|
||||
this._handleDateChange({endDate})
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
super.componentDidMount();
|
||||
|
||||
this._crushHeader();
|
||||
}
|
||||
|
||||
_growHeader() {
|
||||
if (this.expanded) return;
|
||||
|
||||
this.expanded = true;
|
||||
|
||||
this.refs.root.style.minHeight = '245px';
|
||||
this.refs.root.style.maxHeight = '245px';
|
||||
|
||||
this.refs.inside.style.minHeight = '125px';
|
||||
this.refs.inside.style.maxHeight = '125px';
|
||||
this.refs.inside.style.height = '125px';
|
||||
|
||||
this.refs.arrow.classList.remove('rotate-down');
|
||||
|
||||
clearTimeout(this.tid);
|
||||
this.tid = setTimeout(() => {
|
||||
this.refs.query.classList.remove('visibility-hidden');
|
||||
this.refs.select.classList.remove('visibility-hidden');
|
||||
}, 450);
|
||||
}
|
||||
|
||||
_crushHeader() {
|
||||
if (!this.expanded) return;
|
||||
|
||||
this.expanded = false;
|
||||
|
||||
this.refs.query.classList.add('visibility-hidden');
|
||||
this.refs.select.classList.add('visibility-hidden');
|
||||
this.refs.arrow.classList.add('rotate-down');
|
||||
|
||||
clearTimeout(this.tid);
|
||||
this.tid = setTimeout(() => {
|
||||
this.refs.root.style.minHeight = '170px';
|
||||
this.refs.root.style.maxHeight = '170px';
|
||||
|
||||
this.refs.inside.style.minHeight = '50px';
|
||||
this.refs.inside.style.maxHeight = '50px';
|
||||
this.refs.inside.style.height = '50px';
|
||||
}, 450);
|
||||
}
|
||||
|
||||
_toggleCrush() {
|
||||
if (this.expanded) {
|
||||
this._crushHeader();
|
||||
} else {
|
||||
this._growHeader();
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const querystring = EntryStore.getQueryString({_id: {$in: this.state.selected}}, []);
|
||||
|
||||
return (
|
||||
<div className={ifcat('header', {hide:this.props.hide})} style={{padding:13}}>
|
||||
<div className="flex">
|
||||
<div style={{minHeight: 245, overflow: 'hidden'}} ref='root' className='animate-height'>
|
||||
<div className='flex' style={{position: 'relative'}}>
|
||||
<input ref='input' style={{paddingRight: 40}} className='big-input' placeholder='Starting typing...'
|
||||
onChange={e => this._handleTextSearch(e.target.value)}/>
|
||||
<span ref='arrow' className='animate-transform arrow-expand icon arrow' onClick={() => this._toggleCrush()}/>
|
||||
</div>
|
||||
<div ref='inside' className={ifcat('header animate-height', {hide:this.props.hide})} style={{padding:13}}>
|
||||
<div className="flex " ref="range">
|
||||
<div className="box">
|
||||
<div className='field flex'>
|
||||
<div className="name box">range</div>
|
||||
<div className="input box">
|
||||
<div className='date-holder'>
|
||||
<DatePicker
|
||||
selected={this.state.startDate}
|
||||
startDate={this.state.startDate}
|
||||
endDate={this.state.endDate}
|
||||
onChange={a => this._handleChangeStartDate(a)}/>
|
||||
<span className='date-to'>to</span>
|
||||
<DatePicker
|
||||
selected={this.state.endDate}
|
||||
startDate={this.state.startDate}
|
||||
endDate={this.state.endDate}
|
||||
onChange={a => this._handleChangeEndDate(a)}/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex animate-visibility" style={{marginTop: 10}} ref="query">
|
||||
<div className="box" style={{marginRight:13}}>
|
||||
<div className='field flex'>
|
||||
<div className="name box">query</div>
|
||||
|
@ -70,7 +238,7 @@ export default class Header extends Influx.Component {
|
|||
<div className="name box">options</div>
|
||||
<div className="input box">
|
||||
<AceEditor width="100%" showPrintMargin={false} showGutter={false} mode="json" height="100%"
|
||||
highlightActiveLine={false} minLines={1} maxLines={1} name="sort" theme="monokai"
|
||||
highlightActiveLine={false} minLines={1} maxLines={1} name="options" theme="monokai"
|
||||
onChange={this._handleChange.bind(this, 'options')}
|
||||
value={JSON.stringify(this.state.search.options)} editorProps={{$blockScrolling: true}}/>
|
||||
</div>
|
||||
|
@ -83,7 +251,36 @@ export default class Header extends Influx.Component {
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex animate-visibility" style={{marginTop: 10}} ref="select">
|
||||
<div className="box" style={{marginRight:13}}>
|
||||
<div className='field flex readonly'>
|
||||
<div className="name box">SELECT</div>
|
||||
<div className="input box">
|
||||
<AceEditor width="100%" showPrintMargin={false} showGutter={false} mode="json" height="100%"
|
||||
highlightActiveLine={false} minLines={1} maxLines={1} name="selected" theme="monokai"
|
||||
readOnly={true} value={JSON.stringify(this.state.selected)}
|
||||
editorProps={{$blockScrolling: true}}/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="box" style={{maxWidth:100,position:'relative', marginRight:13}}>
|
||||
<a className='button' target="_blank" style={{background: '#4CAF50'}}
|
||||
href={`#${querystring}`}>PERMALINK</a>
|
||||
</div>
|
||||
<div className="box" style={{maxWidth:100,position:'relative', marginRight:13}}>
|
||||
<div className='button' onClick={() => Dispatcher.emit(Dispatcher.Events.REQUEST_SELECT_ALL_ENTRIES)}>
|
||||
SELECT ALL
|
||||
</div>
|
||||
</div>
|
||||
<div className="box" style={{maxWidth:100,position:'relative'}}>
|
||||
<div className='button' onClick={() => Dispatcher.emit(Dispatcher.Events.REQUEST_SELECT_CLEAR_ENTRIES)}>
|
||||
CLEAR
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<TimeSeries />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -11,7 +11,7 @@ export default class Sidebar extends Influx.Component {
|
|||
constructor(...args) {
|
||||
super(...args);
|
||||
|
||||
this.state = {date: moment().startOf('day')};
|
||||
this.state = {};
|
||||
}
|
||||
|
||||
getListeners() {
|
||||
|
@ -21,58 +21,49 @@ export default class Sidebar extends Influx.Component {
|
|||
]
|
||||
}
|
||||
|
||||
_onEntryStoreDatabaseReady() {
|
||||
const exposed = Object.keys(config.exposed);
|
||||
this._selectExpose(exposed[0], config.exposed[exposed[0]]);
|
||||
_onEntryStoreDatabaseReady(selected) {
|
||||
const exposed = Object.keys(config.queries);
|
||||
if (!selected) {
|
||||
this._selectExpose(exposed[0], config.queries[exposed[0]]);
|
||||
}
|
||||
}
|
||||
|
||||
_onDispatcherRequestEntrySearch(query, sort) {
|
||||
const {expose} = query;
|
||||
for (const [k, exposed] of Object.entries(config.exposed)) {
|
||||
if (_.isEqual(exposed.query.expose, expose)) {
|
||||
return this.setState({label: exposed.label});
|
||||
const _query = JSON.parse(JSON.stringify(query));
|
||||
delete _query.serialized;
|
||||
|
||||
for (const [k, exposed] of Object.entries(config.queries)) {
|
||||
if (_.isEqual(_query, exposed.query)) {
|
||||
return this.setState({i: k});
|
||||
}
|
||||
}
|
||||
|
||||
this.setState({label: undefined});
|
||||
this.setState({i: undefined});
|
||||
}
|
||||
|
||||
_selectExpose(i, expose, date = this.state.date) {
|
||||
_selectExpose(i, expose) {
|
||||
this.setState({i});
|
||||
Dispatcher.emit(Dispatcher.Events.REQUEST_ENTRY_SEARCH,
|
||||
Object.assign({}, expose.query, {date: this._getRange(date)}));
|
||||
}
|
||||
|
||||
_getRange(date = moment()) {
|
||||
const today = moment(date).startOf('day');
|
||||
const tomorrow = moment(today).add(1, 'days');
|
||||
|
||||
return {$gte: today.toISOString(), $lt: tomorrow.toISOString()};
|
||||
}
|
||||
|
||||
_selectDate(date) {
|
||||
const query = EntryStore.getSearchQuery();
|
||||
this.setState({date});
|
||||
Dispatcher.emit(Dispatcher.Events.REQUEST_ENTRY_SEARCH,
|
||||
Object.assign({}, query, {date: this._getRange(date)}));
|
||||
Dispatcher.emit(Dispatcher.Events.REQUEST_ENTRY_SEARCH, Object.assign({}, expose.query));
|
||||
}
|
||||
|
||||
render() {
|
||||
|
||||
const exposed = _.map(config.exposed, (expose, i)=> {
|
||||
return <div key={i} className={ifcat('item', {selected:this.state.i === i})}
|
||||
const exposed = _.map(config.queries, (expose, i)=> {
|
||||
return <div key={i} className={ifcat('item', {selected: this.state.i === i})}
|
||||
onClick={this._selectExpose.bind(this, i, expose, this.state.date)}>{expose.label}</div>;
|
||||
});
|
||||
|
||||
|
||||
exposed.push(<div key={'__custom__'} style={{cursor: 'default'}}
|
||||
className={ifcat('item', {selected: !this.state.i})}>USER-DEFINED</div>);
|
||||
|
||||
return (
|
||||
<div className={ifcat('sidebar box',{hide:this.props.hide})}>
|
||||
<div className="group">
|
||||
<div className="title">Exposed <DatePicker onChange={this._selectDate.bind(this)}
|
||||
selected={this.state.date}/>
|
||||
</div>
|
||||
{exposed}
|
||||
</div>
|
||||
<div className={ifcat('sidebar box',{hide:this.props.hide})}>
|
||||
<div className="group">
|
||||
<a className="title" href={`${window.location.origin}${window.location.pathname}`}>Scribe.js</a>
|
||||
{exposed}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
135
asr/viewer/components/views/Timeseries.jsx
Normal file
135
asr/viewer/components/views/Timeseries.jsx
Normal file
|
@ -0,0 +1,135 @@
|
|||
import React from 'react'
|
||||
import Influx from 'react-influx'
|
||||
import {ifcat} from '../../libs/utils'
|
||||
import EntryStore from '../../stores/EntryStore'
|
||||
import Dispatcher from '../../dispatchers/Dispatcher'
|
||||
import Spinner from 'react-spinkit'
|
||||
import _ from 'underscore';
|
||||
import moment from 'moment';
|
||||
|
||||
const GROWTH = 1;
|
||||
|
||||
export default class TimeSeries extends Influx.Component {
|
||||
constructor(...args) {
|
||||
super(...args);
|
||||
|
||||
this.state = {data: [], series: []};
|
||||
}
|
||||
|
||||
getListeners() {
|
||||
return [
|
||||
[EntryStore, EntryStore.Events.UPDATED_TIMESERIES, this._onEntryStoreUpdatedTimeSeries],
|
||||
[Dispatcher, Dispatcher.Events.REQUEST_TIMESERIES_DATE, this._onDispatcherRequestTimeSeriesDate]
|
||||
]
|
||||
}
|
||||
|
||||
_onDispatcherRequestTimeSeriesDate(date) {
|
||||
date = moment(date);
|
||||
const {series} = this.state;
|
||||
|
||||
for (var i = 0; i < series.length; i++) {
|
||||
const data = series[i];
|
||||
|
||||
// console.log(data.date.toDate(), date.toDate());
|
||||
|
||||
if (data.date.diff(date) < 0) {
|
||||
Array.prototype.forEach.call(document.getElementsByClassName('red-selected'), a => a.classList.remove('red-selected'));
|
||||
this.refs[data.ref].classList.add('red-selected');
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_onEntryStoreUpdatedTimeSeries() {
|
||||
const period = EntryStore.getSearchPeriod();
|
||||
const aggregations = EntryStore.getSearchTimeSeries();
|
||||
const data = _.groupBy(aggregations, a => `${a._id.hour.day}_${a._id.hour.hour}`);
|
||||
const end = moment(period.$lt);
|
||||
const series = [];
|
||||
const exposers = Object.keys(_.indexBy(aggregations, a => a._id.expose));
|
||||
|
||||
let start = moment(period.$gt);
|
||||
let max = 0;
|
||||
|
||||
for (var i in data) {
|
||||
const exposed = {};
|
||||
for (var k = 0; k < data[i].length; k++) {
|
||||
const count = data[i][k].number;
|
||||
exposed[data[i][k]._id.expose] = count;
|
||||
|
||||
max = Math.max(max, count);
|
||||
}
|
||||
|
||||
data[i] = exposed;
|
||||
}
|
||||
|
||||
let count = 0;
|
||||
const offset = new Date().getTimezoneOffset() * 60 * 1000;
|
||||
|
||||
while (end.diff(start) > 0) {
|
||||
const startOffset = moment(start.toDate().getTime() + offset);
|
||||
const day = startOffset.date();
|
||||
const hour = startOffset.hour();
|
||||
const key = `${day}_${hour}`;
|
||||
|
||||
series.push({date: moment(start), ref: `pos-${count++}`, count: data[key] || {}});
|
||||
start = start.add(1, 'hour');
|
||||
}
|
||||
|
||||
var crushSeries = [];
|
||||
var groupSize = parseInt(series.length / 24);
|
||||
for (var i = 0; i < series.length; i += groupSize) {
|
||||
const start = series[i];
|
||||
for (var k = i + 1; k < i + groupSize; k++) {
|
||||
const curr = series[k];
|
||||
for (var e = 0; e < exposers.length; e++) {
|
||||
const ee = exposers[e];
|
||||
start.count[ee] = start.count[ee] || 0;
|
||||
start.count[ee] += (curr.count[ee] || 0)
|
||||
|
||||
max = Math.max(start.count[ee], max);
|
||||
}
|
||||
}
|
||||
|
||||
crushSeries.push(start);
|
||||
}
|
||||
|
||||
this.setState({series: crushSeries.reverse(), exposers, max});
|
||||
}
|
||||
|
||||
render() {
|
||||
const maxHeight = 80;
|
||||
const {series, exposers, max} = this.state;
|
||||
|
||||
const elems = series.map((data, i) => {
|
||||
const exposed = exposers.map(expose => {
|
||||
const count = data.count[expose] || 0;
|
||||
const height = Math.min(count / max * maxHeight * GROWTH, maxHeight);
|
||||
|
||||
return (
|
||||
<div key={expose}
|
||||
className={'box graph bar tooltip ' + expose}
|
||||
style={{ top: `${maxHeight - height}px`, height: `${height}px`}}>
|
||||
<span className='tooltiptext tooltip-top'>{`${expose}: ${count}`}</span>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
return (
|
||||
<div key={i} ref={data.ref} className='box graph ggroup'>
|
||||
<div className='flex full gg'>
|
||||
{exposed}
|
||||
<div className='time-series-label'>{data.date.format('M/D hA')}</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
return (
|
||||
<div className='flex'
|
||||
style={{height: maxHeight, width: '100%', position: 'relative', background: 'rgba(0,0,0,0.3)'}}>
|
||||
{elems}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
|
@ -2,10 +2,16 @@ import Influx from 'react-influx'
|
|||
import keyMirror from 'keymirror'
|
||||
|
||||
const Events = keyMirror({
|
||||
REQUEST_INIT_DATABASE: null,
|
||||
REQUEST_INIT_SOCKET: null,
|
||||
REQUEST_ENTRY_SEARCH: null
|
||||
});
|
||||
REQUEST_GROW_SEARCH: null,
|
||||
REQUEST_INIT_DATABASE: null,
|
||||
REQUEST_INIT_SOCKET: null,
|
||||
REQUEST_ENTRY_SEARCH: null,
|
||||
REQUEST_SELECT_ENTRY: null,
|
||||
REQUEST_SELECT_ALL_ENTRIES: null,
|
||||
REQUEST_SELECT_CLEAR_ENTRIES: null,
|
||||
REQUEST_TIMESERIES_DATE: null,
|
||||
REQUEST_PERIOD: null
|
||||
});
|
||||
|
||||
class Dispatcher extends Influx.Dispatcher {
|
||||
// override as needed
|
||||
|
|
|
@ -23,12 +23,18 @@ function JQueryHttpClient(method, url, params, data, success, error) {
|
|||
|
||||
req.end((err, res) => {
|
||||
if (err) return error(err);
|
||||
return success(res.body.docs || []);
|
||||
let docs = [];
|
||||
try {
|
||||
docs = JSON.parse(res.text);
|
||||
} catch (e) {
|
||||
// ignore
|
||||
}
|
||||
return success(docs);
|
||||
}).on('progress', e => console.log('Percentage done: ', e.percent));
|
||||
}
|
||||
|
||||
export default class MongoDB {
|
||||
constructor(url = 'rest/', namespace = 'scribe', client = 'webapp') {
|
||||
constructor(url = 'rest/db/', namespace = 'scribe', client = 'webapp') {
|
||||
this.url = url;
|
||||
this.namespace = namespace;
|
||||
this.client = client;
|
||||
|
|
|
@ -2,55 +2,124 @@ import Influx from 'react-influx'
|
|||
import keyMirror from 'keymirror'
|
||||
import Dispatcher from '../dispatchers/Dispatcher'
|
||||
import * as JSON2 from '../../../src/libs/JSON2'
|
||||
import moment from 'moment';
|
||||
import _ from 'underscore';
|
||||
import request from 'superagent'
|
||||
|
||||
const OVERFLOW = 200;
|
||||
const Events = keyMirror({
|
||||
DATABASE_READY: null,
|
||||
SOCKET_READY: null,
|
||||
UPDATED: null
|
||||
});
|
||||
DATABASE_READY: null,
|
||||
SOCKET_READY: null,
|
||||
UPDATED: null,
|
||||
UPDATED_SELECTED: null,
|
||||
UPDATED_TIMESERIES: null
|
||||
});
|
||||
|
||||
class EntryStore extends Influx.Store {
|
||||
constructor(...args) {
|
||||
super(Dispatcher/*, additional dispatchers you have */);
|
||||
this.data = {entries: [], search: [], history: {}, options: {sort: {date: -1}}};
|
||||
this.data = Object.assign(
|
||||
{
|
||||
entries: [], selected: {},
|
||||
options: {sort: {date: -1}, limit: OVERFLOW},
|
||||
period: {
|
||||
$gt: moment().startOf('day').toISOString(),
|
||||
$lt: moment().add(1, 'days').startOf('day').toISOString()
|
||||
}
|
||||
}, this.fromQueryString(window.location.hash.substr(1)));
|
||||
|
||||
this.setMaxListeners(Number.MAX_SAFE_INTEGER);
|
||||
}
|
||||
|
||||
getSearchResults() {
|
||||
return this.data.search;
|
||||
return this.data.entries;
|
||||
}
|
||||
|
||||
getDispatcherListeners() {
|
||||
return [
|
||||
[Dispatcher, Dispatcher.Events.REQUEST_INIT_DATABASE, this._onDispatcherRequestInitDatabase],
|
||||
[Dispatcher, Dispatcher.Events.REQUEST_INIT_SOCKET, this._onDispatcherRequestInitSocket],
|
||||
[Dispatcher, Dispatcher.Events.REQUEST_ENTRY_SEARCH, this._onDispatcherRequestEntrySearch]
|
||||
[Dispatcher, Dispatcher.Events.REQUEST_PERIOD, this._onDispatcherRequestPeriod],
|
||||
[Dispatcher, Dispatcher.Events.REQUEST_SELECT_ENTRY, this._onDispatcherRequestSelectEntry],
|
||||
[Dispatcher, Dispatcher.Events.REQUEST_SELECT_ALL_ENTRIES, this._onDispatcherRequestSelectAllEntry],
|
||||
[Dispatcher, Dispatcher.Events.REQUEST_SELECT_CLEAR_ENTRIES, this._onDispatcherRequestClearEntries],
|
||||
[Dispatcher, Dispatcher.Events.REQUEST_ENTRY_SEARCH, this._onDispatcherRequestEntrySearch],
|
||||
[Dispatcher, Dispatcher.Events.REQUEST_GROW_SEARCH, this._onDispatcherRequestGrowSearch]
|
||||
]
|
||||
}
|
||||
|
||||
_onDispatcherRequestInitDatabase() {
|
||||
db.Entry.find({}).fetch(()=>this.emit(Events.DATABASE_READY), ()=>this.emit(Events.DATABASE_READY));
|
||||
const presearch = Object.keys(this.data.query || {}).length;
|
||||
this.emit(Events.DATABASE_READY, presearch);
|
||||
|
||||
if (presearch) {
|
||||
Dispatcher.emit(Dispatcher.Events.REQUEST_ENTRY_SEARCH, this.data.query, this.data.options, this.data.period);
|
||||
}
|
||||
}
|
||||
|
||||
_onDispatcherRequestSelectEntry(id, selected) {
|
||||
if (selected) {
|
||||
this.data.selected[id] = true;
|
||||
} else {
|
||||
delete this.data.selected[id];
|
||||
}
|
||||
|
||||
this.emit(Events.UPDATED_SELECTED);
|
||||
this.emit(Events.UPDATED_SELECTED + id, true);
|
||||
}
|
||||
|
||||
isSelected(id) {
|
||||
return !!this.data.selected[id];
|
||||
}
|
||||
|
||||
getSelected() {
|
||||
return Object.keys(this.data.selected);
|
||||
}
|
||||
|
||||
_onDispatcherRequestSelectAllEntry() {
|
||||
const plucked = _.pluck(this.data.entries, '_id');
|
||||
plucked.forEach(a => this.data.selected[a] = true);
|
||||
|
||||
this.emit(Events.UPDATED_SELECTED);
|
||||
plucked.forEach(id => this.emit(Events.UPDATED_SELECTED + id, true));
|
||||
}
|
||||
|
||||
_onDispatcherRequestClearEntries() {
|
||||
const keys = Object.keys(this.data.selected);
|
||||
|
||||
this.data.selected = {};
|
||||
|
||||
this.emit(Events.UPDATED_SELECTED);
|
||||
keys.forEach(id => this.emit(Events.UPDATED_SELECTED + id, false));
|
||||
}
|
||||
|
||||
_onDispatcherRequestInitSocket() {
|
||||
const url = window.location;
|
||||
(config.socketPorts || []).map(port => {
|
||||
this.socket = io(`${url.protocol}//${url.hostname}:${port}`);
|
||||
this.socket.on('connection', function (socket) {
|
||||
console.log('sup!!!');
|
||||
});
|
||||
this.socket.on('data', data => {
|
||||
console.log(data);
|
||||
db.localDb.Entry.upsert(data, () => {
|
||||
this.searchEntries(this.data.query, this.getSearchOptions());
|
||||
});
|
||||
data._pushed = true;
|
||||
db.localDb.Entry.upsert(data, () => this.refreshSession());
|
||||
});
|
||||
});
|
||||
|
||||
this.emit(Events.SOCKET_READY);
|
||||
}
|
||||
|
||||
_onDispatcherRequestEntrySearch(query, options) {
|
||||
this.searchEntries(query, options);
|
||||
_onDispatcherRequestEntrySearch(...args) {
|
||||
this.startSession(...args);
|
||||
}
|
||||
|
||||
_onDispatcherRequestGrowSearch() {
|
||||
this.fetchSession();
|
||||
}
|
||||
|
||||
_onDispatcherRequestPeriod(period) {
|
||||
this.setSessionPeriod(period);
|
||||
}
|
||||
|
||||
getSearchTimeSeries() {
|
||||
return this.data.timeseries;
|
||||
}
|
||||
|
||||
getSearchQuery() {
|
||||
|
@ -65,25 +134,124 @@ class EntryStore extends Influx.Store {
|
|||
setTimeout(() => super.emit(...args), 0);
|
||||
}
|
||||
|
||||
searchEntries(query = {}, options = this.data.options, remote = false) {
|
||||
this.data.query = query;
|
||||
this.data.options = options;
|
||||
addEntries(entries, pre) {
|
||||
this.data.entries = pre ? [...entries, ...this.data.entries] : [...this.data.entries, ...entries];
|
||||
}
|
||||
|
||||
const searchLocal = ()=> {
|
||||
db.localDb.Entry.find(query, Object.assign({}, options)).fetch(entries => {
|
||||
this.data.search = entries.map(e => JSON2.parse(JSON.stringify(e)));
|
||||
fromQueryString(string) {
|
||||
try {
|
||||
return JSON.parse(string);
|
||||
} catch (e) {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
getQueryString(query = this.data.query, select = this.data.select) {
|
||||
return JSON.stringify({
|
||||
query: query,
|
||||
options: this.data.options,
|
||||
period: this.data.period,
|
||||
text: this.data.text,
|
||||
select: select
|
||||
});
|
||||
}
|
||||
|
||||
getSessionDBQuery(date = this.data.period) {
|
||||
return Object.assign({}, Object.assign({}, this.data.query, this.data.text ? {
|
||||
serialized: {
|
||||
$regex: `.*${this.data.text}.*`,
|
||||
$options: 'i'
|
||||
}
|
||||
} : {}), {date});
|
||||
}
|
||||
|
||||
getSessionDBOptionsQuery(options = this.data.options) {
|
||||
return Object.assign({}, this.data.options, options);
|
||||
}
|
||||
|
||||
startSession(query = this.data.query, options = this.data.options, period = this.data.period, text = this.data.text) {
|
||||
this.data.entries = [];
|
||||
this.data.options = options;
|
||||
this.data.query = query;
|
||||
this.data.finished = false;
|
||||
this.data.timeseries = [];
|
||||
this.data.period = period;
|
||||
this.data.text = text;
|
||||
this.data.session = Date.now();
|
||||
|
||||
db.Entry.find(this.getSessionDBQuery(), this.getSessionDBOptionsQuery({skip: 0}))
|
||||
.fetch(entries => {
|
||||
this.data.entries = entries;
|
||||
this.emit(Events.UPDATED);
|
||||
});
|
||||
};
|
||||
|
||||
const dateKey = JSON.stringify(query.date);
|
||||
remote = remote || !this.data.history[dateKey];
|
||||
|
||||
if (remote) {
|
||||
this.data.history[dateKey] = true;
|
||||
db.Entry.find({date: query.date}, Object.assign({}, options)).fetch(searchLocal);
|
||||
} else {
|
||||
searchLocal();
|
||||
request
|
||||
.post('rest/timeseries')
|
||||
.send(this.getSessionDBQuery())
|
||||
.end((err, res) => {
|
||||
this.data.timeseries = res.body || [];
|
||||
this.emit(Events.UPDATED_TIMESERIES);
|
||||
});
|
||||
|
||||
window.location.hash = this.getQueryString();
|
||||
}
|
||||
|
||||
getSearchPeriod() {
|
||||
return this.data.period;
|
||||
}
|
||||
|
||||
getSessionText() {
|
||||
return this.data.text || '';
|
||||
}
|
||||
|
||||
setSessionText(text) {
|
||||
this.data.text = text;
|
||||
|
||||
this.startSession(); // restart
|
||||
}
|
||||
|
||||
setSessionPeriod(period) {
|
||||
this.data.period = period;
|
||||
|
||||
this.startSession(); // restart
|
||||
}
|
||||
|
||||
getSessionId() {
|
||||
return this.data.session;
|
||||
}
|
||||
|
||||
refreshSession() {
|
||||
db.localDb.Entry
|
||||
.find(this.getSessionDBQuery(), this.getSessionDBOptionsQuery({limit: null, skip: 0}))
|
||||
.fetch(entries => {
|
||||
this.data.entries = entries;
|
||||
this.emit(Events.UPDATED);
|
||||
});
|
||||
}
|
||||
|
||||
fetchSession(up) {
|
||||
const {options, entries, query} = this.data;
|
||||
const entry = entries[entries.length - 1];
|
||||
|
||||
if (entry) {
|
||||
const periodCpy = JSON.parse(JSON.stringify(this.data.period));
|
||||
if (up) {
|
||||
delete periodCpy.$gte;
|
||||
delete periodCpy.$gt;
|
||||
periodCpy.$gte = entry.date;
|
||||
} else {
|
||||
delete periodCpy.$lte;
|
||||
delete periodCpy.$lt;
|
||||
periodCpy.$lte = entry.date;
|
||||
}
|
||||
|
||||
db
|
||||
.Entry
|
||||
.find(this.getSessionDBQuery(periodCpy), this.getSessionDBOptionsQuery())
|
||||
.fetch(() => {
|
||||
this.refreshSession();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
144
deps.json
144
deps.json
|
@ -1,4 +1,14 @@
|
|||
[
|
||||
{
|
||||
"department": "kessler",
|
||||
"relatedTo": "stuff",
|
||||
"name": "JSONStream",
|
||||
"licensePeriod": "perpetual",
|
||||
"material": "material",
|
||||
"licenseType": "(MIT OR Apache-2.0)",
|
||||
"link": "git://github.com/dominictarr/JSONStream.git",
|
||||
"comment": "1.1.1"
|
||||
},
|
||||
{
|
||||
"department": "kessler",
|
||||
"relatedTo": "stuff",
|
||||
|
@ -9,6 +19,116 @@
|
|||
"link": "git+https://github.com/caolan/async.git",
|
||||
"comment": "1.5.2"
|
||||
},
|
||||
{
|
||||
"department": "kessler",
|
||||
"relatedTo": "stuff",
|
||||
"name": "babel-plugin-syntax-async-functions",
|
||||
"licensePeriod": "perpetual",
|
||||
"material": "material",
|
||||
"licenseType": "MIT",
|
||||
"link": "https://github.com/babel/babel/tree/master/packages/babel-plugin-syntax-async-functions",
|
||||
"comment": "6.5.0"
|
||||
},
|
||||
{
|
||||
"department": "kessler",
|
||||
"relatedTo": "stuff",
|
||||
"name": "babel-plugin-syntax-class-properties",
|
||||
"licensePeriod": "perpetual",
|
||||
"material": "material",
|
||||
"licenseType": "MIT",
|
||||
"link": "https://github.com/babel/babel/tree/master/packages/babel-plugin-syntax-class-properties",
|
||||
"comment": "6.5.0"
|
||||
},
|
||||
{
|
||||
"department": "kessler",
|
||||
"relatedTo": "stuff",
|
||||
"name": "babel-plugin-transform-async-functions",
|
||||
"licensePeriod": "perpetual",
|
||||
"material": "material",
|
||||
"licenseType": "MIT",
|
||||
"link": "https://github.com/babel/babel/tree/master/packages/babel-plugin-transform-async-functions",
|
||||
"comment": "6.5.0"
|
||||
},
|
||||
{
|
||||
"department": "kessler",
|
||||
"relatedTo": "stuff",
|
||||
"name": "babel-plugin-transform-async-to-module-method",
|
||||
"licensePeriod": "perpetual",
|
||||
"material": "material",
|
||||
"licenseType": "MIT",
|
||||
"link": "https://github.com/babel/babel/tree/master/packages/babel-plugin-transform-async-to-module-method",
|
||||
"comment": "6.7.0"
|
||||
},
|
||||
{
|
||||
"department": "kessler",
|
||||
"relatedTo": "stuff",
|
||||
"name": "babel-plugin-transform-class-properties",
|
||||
"licensePeriod": "perpetual",
|
||||
"material": "material",
|
||||
"licenseType": "MIT",
|
||||
"link": "https://github.com/babel/babel/tree/master/packages/babel-plugin-transform-class-properties",
|
||||
"comment": "6.5.0"
|
||||
},
|
||||
{
|
||||
"department": "kessler",
|
||||
"relatedTo": "stuff",
|
||||
"name": "babel-plugin-transform-export-default-name",
|
||||
"licensePeriod": "perpetual",
|
||||
"material": "material",
|
||||
"licenseType": "BSD-3-Clause",
|
||||
"link": "git+https://github.com/gajus/babel-plugin-transform-export-default-name.git",
|
||||
"comment": "1.0.4"
|
||||
},
|
||||
{
|
||||
"department": "kessler",
|
||||
"relatedTo": "stuff",
|
||||
"name": "babel-plugin-transform-object-rest-spread",
|
||||
"licensePeriod": "perpetual",
|
||||
"material": "material",
|
||||
"licenseType": "MIT",
|
||||
"link": "https://github.com/babel/babel/tree/master/packages/babel-plugin-transform-object-rest-spread",
|
||||
"comment": "6.5.0"
|
||||
},
|
||||
{
|
||||
"department": "kessler",
|
||||
"relatedTo": "stuff",
|
||||
"name": "babel-plugin-transform-runtime",
|
||||
"licensePeriod": "perpetual",
|
||||
"material": "material",
|
||||
"licenseType": "MIT",
|
||||
"link": "https://github.com/babel/babel/tree/master/packages/babel-plugin-transform-runtime",
|
||||
"comment": "6.5.2"
|
||||
},
|
||||
{
|
||||
"department": "kessler",
|
||||
"relatedTo": "stuff",
|
||||
"name": "babel-preset-es2015",
|
||||
"licensePeriod": "perpetual",
|
||||
"material": "material",
|
||||
"licenseType": "MIT",
|
||||
"link": "https://github.com/babel/babel/tree/master/packages/babel-preset-es2015",
|
||||
"comment": "6.5.0"
|
||||
},
|
||||
{
|
||||
"department": "kessler",
|
||||
"relatedTo": "stuff",
|
||||
"name": "babel-preset-react",
|
||||
"licensePeriod": "perpetual",
|
||||
"material": "material",
|
||||
"licenseType": "MIT",
|
||||
"link": "https://github.com/babel/babel/tree/master/packages/babel-preset-react",
|
||||
"comment": "6.5.0"
|
||||
},
|
||||
{
|
||||
"department": "kessler",
|
||||
"relatedTo": "stuff",
|
||||
"name": "babel-register",
|
||||
"licensePeriod": "perpetual",
|
||||
"material": "material",
|
||||
"licenseType": "MIT",
|
||||
"link": "https://github.com/babel/babel/tree/master/packages/babel-register",
|
||||
"comment": "6.5.2"
|
||||
},
|
||||
{
|
||||
"department": "kessler",
|
||||
"relatedTo": "stuff",
|
||||
|
@ -29,6 +149,26 @@
|
|||
"link": "https://github.com/jshttp/basic-auth",
|
||||
"comment": "1.0.3"
|
||||
},
|
||||
{
|
||||
"department": "kessler",
|
||||
"relatedTo": "stuff",
|
||||
"name": "basic-auth-connect",
|
||||
"licensePeriod": "perpetual",
|
||||
"material": "material",
|
||||
"licenseType": "MIT",
|
||||
"link": "https://github.com/expressjs/basic-auth-connect.git",
|
||||
"comment": "1.0.0"
|
||||
},
|
||||
{
|
||||
"department": "kessler",
|
||||
"relatedTo": "stuff",
|
||||
"name": "bluebird",
|
||||
"licensePeriod": "perpetual",
|
||||
"material": "material",
|
||||
"licenseType": "MIT",
|
||||
"link": "git://github.com/petkaantonov/bluebird.git",
|
||||
"comment": "3.3.4"
|
||||
},
|
||||
{
|
||||
"department": "kessler",
|
||||
"relatedTo": "stuff",
|
||||
|
@ -216,7 +356,7 @@
|
|||
"material": "material",
|
||||
"licenseType": "MIT",
|
||||
"link": "git://github.com/Hacker0x01/react-datepicker.git",
|
||||
"comment": "0.18.0"
|
||||
"comment": "0.25.0"
|
||||
},
|
||||
{
|
||||
"department": "kessler",
|
||||
|
@ -236,7 +376,7 @@
|
|||
"material": "material",
|
||||
"licenseType": "ISC",
|
||||
"link": "git+https://github.com/bluejamesbond/Influx.js.git",
|
||||
"comment": "1.0.8"
|
||||
"comment": "1.1.5"
|
||||
},
|
||||
{
|
||||
"department": "kessler",
|
||||
|
|
89
dist/libs/JSON2.js
vendored
89
dist/libs/JSON2.js
vendored
|
@ -4,10 +4,18 @@ Object.defineProperty(exports, "__esModule", {
|
|||
value: true
|
||||
});
|
||||
|
||||
var _keys = require('babel-runtime/core-js/object/keys');
|
||||
|
||||
var _keys2 = _interopRequireDefault(_keys);
|
||||
|
||||
var _stringify = require('babel-runtime/core-js/json/stringify');
|
||||
|
||||
var _stringify2 = _interopRequireDefault(_stringify);
|
||||
|
||||
var _typeof2 = require('babel-runtime/helpers/typeof');
|
||||
|
||||
var _typeof3 = _interopRequireDefault(_typeof2);
|
||||
|
||||
var _promise = require('babel-runtime/core-js/promise');
|
||||
|
||||
var _promise2 = _interopRequireDefault(_promise);
|
||||
|
@ -19,7 +27,7 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
|
|||
|
||||
function parse(str, reviver) {
|
||||
return JSON.parse(str, function (k, v) {
|
||||
var rv = undefined;
|
||||
var rv = void 0;
|
||||
if (typeof v === 'string') {
|
||||
if (/^Promise \{(.*?)}$/g.test(v)) {
|
||||
return new _promise2.default(function (r, e) {
|
||||
|
@ -27,7 +35,7 @@ function parse(str, reviver) {
|
|||
});
|
||||
} else if (/^function ([a-zA-Z0-9_\$]*?)\((.*?)\)\s?\{(.*?)}$/g.test(rv = v.replace(/\n/g, ''))) {
|
||||
try {
|
||||
var x = undefined;
|
||||
var x = void 0;
|
||||
eval('x = ' + v);
|
||||
if (x) {
|
||||
return x;
|
||||
|
@ -50,8 +58,77 @@ function parse(str, reviver) {
|
|||
});
|
||||
}
|
||||
|
||||
var stringifyOnce = function stringifyOnce(obj, replacer, indent) {
|
||||
var printedObjects = [];
|
||||
var printedObjectKeys = [];
|
||||
|
||||
function printOnceReplacer(key, value) {
|
||||
if (printedObjects.length > 2000) {
|
||||
// browsers will not print more than 20K, I don't see the point to allow 2K.. algorithm will not be fast anyway if we have too many objects
|
||||
return 'object too long';
|
||||
}
|
||||
var printedObjIndex = false;
|
||||
printedObjects.forEach(function (obj, index) {
|
||||
if (obj === value) {
|
||||
printedObjIndex = index;
|
||||
}
|
||||
});
|
||||
|
||||
if (key == '') {
|
||||
//root element
|
||||
printedObjects.push(obj);
|
||||
printedObjectKeys.push("root");
|
||||
return value;
|
||||
} else if (printedObjIndex + "" != "false" && (typeof value === 'undefined' ? 'undefined' : (0, _typeof3.default)(value)) == "object") {
|
||||
if (printedObjectKeys[printedObjIndex] == "root") {
|
||||
return "(pointer to root)";
|
||||
} else {
|
||||
return "(see " + (!!value && !!value.constructor ? value.constructor.name.toLowerCase() : typeof value === 'undefined' ? 'undefined' : (0, _typeof3.default)(value)) + " with key " + printedObjectKeys[printedObjIndex] + ")";
|
||||
}
|
||||
} else {
|
||||
|
||||
var qualifiedKey = key || "(empty key)";
|
||||
printedObjects.push(value);
|
||||
printedObjectKeys.push(qualifiedKey);
|
||||
if (replacer) {
|
||||
return replacer(key, value);
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (0, _stringify2.default)(obj, printOnceReplacer, indent);
|
||||
};
|
||||
|
||||
// http://www.z-car.com/blog/programming/how-to-escape-mongo-keys-using-node-js-in-a-flash
|
||||
function escapeKeys(obj) {
|
||||
if (!(Boolean(obj) && (typeof obj === 'undefined' ? 'undefined' : (0, _typeof3.default)(obj)) == 'object' && (0, _keys2.default)(obj).length > 0)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
(0, _keys2.default)(obj).forEach(function (key) {
|
||||
if ((0, _typeof3.default)(obj[key]) == 'object') {
|
||||
escapeKeys(obj[key]);
|
||||
} else {
|
||||
if (key.indexOf('.') !== -1) {
|
||||
var newkey = key.replace(/\./g, '_dot_');
|
||||
obj[newkey] = obj[key];
|
||||
delete obj[key];
|
||||
}
|
||||
if (key.indexOf('$') !== -1) {
|
||||
var newkey = key.replace(/\$/g, '_amp_');
|
||||
obj[newkey] = obj[key];
|
||||
delete obj[key];
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function stringify(obj, replacer, spaces) {
|
||||
return (0, _stringify2.default)(obj, function (k, v) {
|
||||
var objStr = stringifyOnce(obj, function (k, v) {
|
||||
if (typeof v !== 'undefined' && v !== null) {
|
||||
if (typeof v.then === 'function') {
|
||||
return 'Promise { <pending> }';
|
||||
|
@ -64,4 +141,10 @@ function stringify(obj, replacer, spaces) {
|
|||
|
||||
return v;
|
||||
}, spaces);
|
||||
|
||||
var objCpy = JSON.parse(objStr);
|
||||
|
||||
escapeKeys(objCpy);
|
||||
|
||||
return (0, _stringify2.default)(objCpy);
|
||||
}
|
||||
|
|
12
dist/libs/MultiValueMap.js
vendored
12
dist/libs/MultiValueMap.js
vendored
|
@ -18,14 +18,14 @@ var _createClass3 = _interopRequireDefault(_createClass2);
|
|||
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
|
||||
var _class = function () {
|
||||
function _class() {
|
||||
(0, _classCallCheck3.default)(this, _class);
|
||||
var multiValueMap = function () {
|
||||
function multiValueMap() {
|
||||
(0, _classCallCheck3.default)(this, multiValueMap);
|
||||
|
||||
this._map = new _map2.default();
|
||||
}
|
||||
|
||||
(0, _createClass3.default)(_class, [{
|
||||
(0, _createClass3.default)(multiValueMap, [{
|
||||
key: "get",
|
||||
value: function get(key) {
|
||||
if (!this._map.has(key)) {
|
||||
|
@ -64,7 +64,7 @@ var _class = function () {
|
|||
this._map.remove(old);
|
||||
}
|
||||
}]);
|
||||
return _class;
|
||||
return multiValueMap;
|
||||
}();
|
||||
|
||||
exports.default = _class;
|
||||
exports.default = multiValueMap;
|
||||
|
|
14
dist/libs/time.js
vendored
Normal file
14
dist/libs/time.js
vendored
Normal file
|
@ -0,0 +1,14 @@
|
|||
'use strict';
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.default = time;
|
||||
function time() {
|
||||
if (typeof process !== 'undefined' && typeof process.hrtime === 'function') {
|
||||
var duration = process.hrtime();
|
||||
return duration[0] * 1000 + duration[1] / 1e6;
|
||||
}
|
||||
|
||||
return Date.now();
|
||||
}
|
85
dist/middleware/ExpressLogger.js
vendored
Normal file
85
dist/middleware/ExpressLogger.js
vendored
Normal file
|
@ -0,0 +1,85 @@
|
|||
'use strict';
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
|
||||
var _getIterator2 = require('babel-runtime/core-js/get-iterator');
|
||||
|
||||
var _getIterator3 = _interopRequireDefault(_getIterator2);
|
||||
|
||||
var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck');
|
||||
|
||||
var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
|
||||
|
||||
var _createClass2 = require('babel-runtime/helpers/createClass');
|
||||
|
||||
var _createClass3 = _interopRequireDefault(_createClass2);
|
||||
|
||||
var _onFinished = require('on-finished');
|
||||
|
||||
var _onFinished2 = _interopRequireDefault(_onFinished);
|
||||
|
||||
var _time = require('../libs/time');
|
||||
|
||||
var _time2 = _interopRequireDefault(_time);
|
||||
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
|
||||
var ExpressLogger = function () {
|
||||
function ExpressLogger(scribe) {
|
||||
(0, _classCallCheck3.default)(this, ExpressLogger);
|
||||
|
||||
this.options = scribe.module('middleware/ExpressLogger').options;
|
||||
this.scribe = scribe;
|
||||
}
|
||||
|
||||
(0, _createClass3.default)(ExpressLogger, [{
|
||||
key: 'getMiddleware',
|
||||
value: function getMiddleware() {
|
||||
var scribe = this.scribe;
|
||||
var options = this.options;
|
||||
|
||||
|
||||
return function (req, res, next) {
|
||||
var start = (0, _time2.default)();
|
||||
(0, _onFinished2.default)(res, function () {
|
||||
var originalUrl = req.originalUrl;
|
||||
var _iteratorNormalCompletion = true;
|
||||
var _didIteratorError = false;
|
||||
var _iteratorError = undefined;
|
||||
|
||||
try {
|
||||
for (var _iterator = (0, _getIterator3.default)(options.ignore), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
|
||||
var k = _step.value;
|
||||
|
||||
if (new RegExp(k).test(originalUrl)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
_didIteratorError = true;
|
||||
_iteratorError = err;
|
||||
} finally {
|
||||
try {
|
||||
if (!_iteratorNormalCompletion && _iterator.return) {
|
||||
_iterator.return();
|
||||
}
|
||||
} finally {
|
||||
if (_didIteratorError) {
|
||||
throw _iteratorError;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
scribe[options.expose]({ req: req, res: res, duration: (0, _time2.default)() - start });
|
||||
});
|
||||
|
||||
next();
|
||||
};
|
||||
}
|
||||
}]);
|
||||
return ExpressLogger;
|
||||
}();
|
||||
|
||||
exports.default = ExpressLogger;
|
16
dist/middleware/index.js
vendored
Normal file
16
dist/middleware/index.js
vendored
Normal file
|
@ -0,0 +1,16 @@
|
|||
'use strict';
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
|
||||
var _ExpressLogger = require('./ExpressLogger');
|
||||
|
||||
Object.defineProperty(exports, 'ExpressLogger', {
|
||||
enumerable: true,
|
||||
get: function get() {
|
||||
return _interopRequireDefault(_ExpressLogger).default;
|
||||
}
|
||||
});
|
||||
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
@ -8,10 +8,6 @@ var _assign = require('babel-runtime/core-js/object/assign');
|
|||
|
||||
var _assign2 = _interopRequireDefault(_assign);
|
||||
|
||||
var _getIterator2 = require('babel-runtime/core-js/get-iterator');
|
||||
|
||||
var _getIterator3 = _interopRequireDefault(_getIterator2);
|
||||
|
||||
var _map = require('babel-runtime/core-js/map');
|
||||
|
||||
var _map2 = _interopRequireDefault(_map);
|
||||
|
@ -44,28 +40,19 @@ var _callsite = require('callsite');
|
|||
|
||||
var _callsite2 = _interopRequireDefault(_callsite);
|
||||
|
||||
var _onFinished = require('on-finished');
|
||||
var _time2 = require('../libs/time');
|
||||
|
||||
var _onFinished2 = _interopRequireDefault(_onFinished);
|
||||
var _time3 = _interopRequireDefault(_time2);
|
||||
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
|
||||
function _time() {
|
||||
if (typeof process !== 'undefined' && typeof process.hrtime === 'function') {
|
||||
var duration = process.hrtime();
|
||||
return duration[0] * 1000 + duration[1] / 1e6;
|
||||
}
|
||||
var basicConsole = function (_Console) {
|
||||
(0, _inherits3.default)(basicConsole, _Console);
|
||||
|
||||
return Date.now();
|
||||
}
|
||||
function basicConsole(app, id) {
|
||||
(0, _classCallCheck3.default)(this, basicConsole);
|
||||
|
||||
var _class = function (_Console) {
|
||||
(0, _inherits3.default)(_class, _Console);
|
||||
|
||||
function _class(app, id) {
|
||||
(0, _classCallCheck3.default)(this, _class);
|
||||
|
||||
var _this = (0, _possibleConstructorReturn3.default)(this, (0, _getPrototypeOf2.default)(_class).call(this, app, id));
|
||||
var _this = (0, _possibleConstructorReturn3.default)(this, (0, _getPrototypeOf2.default)(basicConsole).call(this, app, id));
|
||||
|
||||
_this._times = new _map2.default();
|
||||
|
||||
|
@ -80,7 +67,7 @@ var _class = function (_Console) {
|
|||
return _this;
|
||||
}
|
||||
|
||||
(0, _createClass3.default)(_class, [{
|
||||
(0, _createClass3.default)(basicConsole, [{
|
||||
key: 'date',
|
||||
value: function date() {
|
||||
this.transient('date', new Date());
|
||||
|
@ -88,56 +75,10 @@ var _class = function (_Console) {
|
|||
}, {
|
||||
key: 'time',
|
||||
value: function time(label) {
|
||||
this._times.set(label, _time());
|
||||
this._times.set(label, (0, _time3.default)());
|
||||
|
||||
return this;
|
||||
}
|
||||
}, {
|
||||
key: 'middleware',
|
||||
value: function middleware() {
|
||||
var _this2 = this;
|
||||
|
||||
var expose = arguments.length <= 0 || arguments[0] === undefined ? 'express' : arguments[0];
|
||||
var ignore = arguments.length <= 1 || arguments[1] === undefined ? [/(scribe)/g] : arguments[1];
|
||||
|
||||
this.expose(expose);
|
||||
|
||||
ignore = Array.isArray(ignore) ? ignore : [ignore];
|
||||
|
||||
return function (req, res, next) {
|
||||
var start = _time();
|
||||
(0, _onFinished2.default)(res, function () {
|
||||
var originalUrl = req.originalUrl;
|
||||
var _iteratorNormalCompletion = true;
|
||||
var _didIteratorError = false;
|
||||
var _iteratorError = undefined;
|
||||
|
||||
try {
|
||||
for (var _iterator = (0, _getIterator3.default)(ignore), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
|
||||
var k = _step.value;
|
||||
|
||||
if (new RegExp(k).test(originalUrl)) return;
|
||||
}
|
||||
} catch (err) {
|
||||
_didIteratorError = true;
|
||||
_iteratorError = err;
|
||||
} finally {
|
||||
try {
|
||||
if (!_iteratorNormalCompletion && _iterator.return) {
|
||||
_iterator.return();
|
||||
}
|
||||
} finally {
|
||||
if (_didIteratorError) {
|
||||
throw _iteratorError;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_this2[expose]({ req: req, res: res, duration: _time() - start });
|
||||
});
|
||||
next();
|
||||
};
|
||||
}
|
||||
}, {
|
||||
key: 'tag',
|
||||
value: function tag() {
|
||||
|
@ -153,8 +94,13 @@ var _class = function (_Console) {
|
|||
key: 'metric',
|
||||
value: function metric() {
|
||||
var map = {};
|
||||
for (var i = 1; i < arguments.length; i += 2) {
|
||||
map[arguments.length <= i - 1 + 0 ? undefined : arguments[i - 1 + 0]] = arguments.length <= i + 0 ? undefined : arguments[i + 0];
|
||||
|
||||
for (var _len2 = arguments.length, metrics = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
|
||||
metrics[_key2] = arguments[_key2];
|
||||
}
|
||||
|
||||
for (var i = 1; i < metrics.length; i += 2) {
|
||||
map[metrics[i - 1]] = metrics[i];
|
||||
}
|
||||
|
||||
this.transient('metrics', (0, _assign2.default)(this.transient('metrics') || {}, map));
|
||||
|
@ -164,7 +110,7 @@ var _class = function (_Console) {
|
|||
}, {
|
||||
key: 'trace',
|
||||
value: function trace(text) {
|
||||
this.transient('callsite', this.callSite(4));
|
||||
this.transient('callsite', this.callSite(2));
|
||||
return this.out('trace', new Error(text));
|
||||
}
|
||||
}, {
|
||||
|
@ -175,16 +121,16 @@ var _class = function (_Console) {
|
|||
throw new Error('No such label: ' + label);
|
||||
}
|
||||
|
||||
this.captureStackTrace();
|
||||
|
||||
this._times.delete(label);
|
||||
|
||||
this.transient('callsite', this.callSite(5));
|
||||
|
||||
var duration = _time() - start;
|
||||
var duration = (0, _time3.default)() - start;
|
||||
|
||||
return this.metric(label, duration).timing('');
|
||||
}
|
||||
}]);
|
||||
return _class;
|
||||
return basicConsole;
|
||||
}(_Console3.default);
|
||||
|
||||
exports.default = _class;
|
||||
exports.default = basicConsole;
|
77
dist/readers/Console.js → dist/reader/Console.js
vendored
77
dist/readers/Console.js → dist/reader/Console.js
vendored
|
@ -12,6 +12,10 @@ var _promise = require('babel-runtime/core-js/promise');
|
|||
|
||||
var _promise2 = _interopRequireDefault(_promise);
|
||||
|
||||
var _typeof2 = require('babel-runtime/helpers/typeof');
|
||||
|
||||
var _typeof3 = _interopRequireDefault(_typeof2);
|
||||
|
||||
var _keys = require('babel-runtime/core-js/object/keys');
|
||||
|
||||
var _keys2 = _interopRequireDefault(_keys);
|
||||
|
@ -34,22 +38,44 @@ var _async2 = _interopRequireDefault(_async);
|
|||
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
|
||||
var _class = function () {
|
||||
function _class() {
|
||||
var app = arguments.length <= 0 || arguments[0] === undefined ? 'Scribe' : arguments[0];
|
||||
var id = arguments.length <= 1 || arguments[1] === undefined ? process.pid : arguments[1];
|
||||
var console = function () {
|
||||
function _class(config) {
|
||||
(0, _classCallCheck3.default)(this, _class);
|
||||
|
||||
this._transient = {};
|
||||
this._persistent = {};
|
||||
this._pipelines = {};
|
||||
this._exposed = {};
|
||||
this._config = config;
|
||||
|
||||
this.persistent('app', app);
|
||||
this.persistent('id', id);
|
||||
this.persistent('app', this._config.app);
|
||||
this.persistent('id', this._config.id);
|
||||
}
|
||||
|
||||
(0, _createClass3.default)(_class, [{
|
||||
key: 'module',
|
||||
value: function module(namespace) {
|
||||
var _this = this;
|
||||
|
||||
if (namespace) {
|
||||
var _ret = function () {
|
||||
var options = _this._config.module[namespace] || {};
|
||||
|
||||
options.isSet = function () {
|
||||
return (0, _keys2.default)(options).length;
|
||||
};
|
||||
|
||||
return {
|
||||
v: { options: options }
|
||||
};
|
||||
}();
|
||||
|
||||
if ((typeof _ret === 'undefined' ? 'undefined' : (0, _typeof3.default)(_ret)) === "object") return _ret.v;
|
||||
}
|
||||
|
||||
return this._config;
|
||||
}
|
||||
}, {
|
||||
key: 'transient',
|
||||
value: function transient(key, value) {
|
||||
if (arguments.length === 1) {
|
||||
|
@ -71,13 +97,26 @@ var _class = function () {
|
|||
}
|
||||
}, {
|
||||
key: 'exposed',
|
||||
value: function exposed() {
|
||||
value: function exposed(expose) {
|
||||
if (expose) {
|
||||
return !!this._exposed[expose];
|
||||
}
|
||||
|
||||
return (0, _keys2.default)(this._exposed);
|
||||
}
|
||||
}, {
|
||||
key: 'captureStackTrace',
|
||||
value: function captureStackTrace() {
|
||||
var idx = arguments.length <= 0 || arguments[0] === undefined ? 3 : arguments[0];
|
||||
|
||||
if (!this.transient('callsite')) {
|
||||
return this.transient('callsite', this.callSite(idx));
|
||||
}
|
||||
}
|
||||
}, {
|
||||
key: 'callSite',
|
||||
value: function callSite() {
|
||||
var idx = arguments.length <= 0 || arguments[0] === undefined ? 5 : arguments[0];
|
||||
var idx = arguments.length <= 0 || arguments[0] === undefined ? 3 : arguments[0];
|
||||
|
||||
try {
|
||||
var site = (0, _callsite2.default)()[idx];
|
||||
|
@ -85,7 +124,7 @@ var _class = function () {
|
|||
return {
|
||||
line: site.getLineNumber(),
|
||||
file: site.getFileName(),
|
||||
func: site.getFunctionName()
|
||||
func: site.getFunctionName() || 'anonymous'
|
||||
};
|
||||
|
||||
return site;
|
||||
|
@ -101,22 +140,24 @@ var _class = function () {
|
|||
}, {
|
||||
key: 'out',
|
||||
value: function out(expose) {
|
||||
var _this = this;
|
||||
var _this2 = this;
|
||||
|
||||
for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
|
||||
args[_key - 1] = arguments[_key];
|
||||
}
|
||||
|
||||
this.captureStackTrace();
|
||||
|
||||
return new _promise2.default(function (resolve, reject) {
|
||||
if (!_this._pipelines[expose]) {
|
||||
if (!_this2._pipelines[expose]) {
|
||||
return resolve();
|
||||
}
|
||||
|
||||
_this.preout();
|
||||
_this2.preout();
|
||||
|
||||
var pipelines = _this._pipelines[expose];
|
||||
var transient = _this._transient;
|
||||
var persistent = _this._persistent;
|
||||
var pipelines = _this2._pipelines[expose];
|
||||
var transient = _this2._transient;
|
||||
var persistent = _this2._persistent;
|
||||
var errs = [];
|
||||
var now = new Date();
|
||||
|
||||
|
@ -125,7 +166,7 @@ var _class = function () {
|
|||
}
|
||||
|
||||
if (!transient.callsite) {
|
||||
transient.callsite = _this.callSite();
|
||||
transient.callsite = _this2.callSite();
|
||||
}
|
||||
|
||||
if (!args.length) {
|
||||
|
@ -133,7 +174,7 @@ var _class = function () {
|
|||
}
|
||||
|
||||
// reset before async
|
||||
_this.reset();
|
||||
_this2.reset();
|
||||
|
||||
_async2.default.forEachOfSeries(pipelines, function (pipeline, name, callback) {
|
||||
var input = {
|
||||
|
@ -223,4 +264,4 @@ var _class = function () {
|
|||
return _class;
|
||||
}();
|
||||
|
||||
exports.default = _class;
|
||||
exports.default = console;
|
25
dist/reader/index.js
vendored
Normal file
25
dist/reader/index.js
vendored
Normal file
|
@ -0,0 +1,25 @@
|
|||
'use strict';
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
|
||||
var _BasicConsole = require('./BasicConsole');
|
||||
|
||||
Object.defineProperty(exports, 'BasicConsole', {
|
||||
enumerable: true,
|
||||
get: function get() {
|
||||
return _interopRequireDefault(_BasicConsole).default;
|
||||
}
|
||||
});
|
||||
|
||||
var _Console = require('./Console');
|
||||
|
||||
Object.defineProperty(exports, 'Console', {
|
||||
enumerable: true,
|
||||
get: function get() {
|
||||
return _interopRequireDefault(_Console).default;
|
||||
}
|
||||
});
|
||||
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
16
dist/router/index.js
vendored
Normal file
16
dist/router/index.js
vendored
Normal file
|
@ -0,0 +1,16 @@
|
|||
'use strict';
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
|
||||
var _Viewer = require('./Viewer');
|
||||
|
||||
Object.defineProperty(exports, 'Viewer', {
|
||||
enumerable: true,
|
||||
get: function get() {
|
||||
return _interopRequireDefault(_Viewer).default;
|
||||
}
|
||||
});
|
||||
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
252
dist/router/viewer.js
vendored
Normal file
252
dist/router/viewer.js
vendored
Normal file
|
@ -0,0 +1,252 @@
|
|||
'use strict';
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
|
||||
var _toConsumableArray2 = require('babel-runtime/helpers/toConsumableArray');
|
||||
|
||||
var _toConsumableArray3 = _interopRequireDefault(_toConsumableArray2);
|
||||
|
||||
var _maxSafeInteger = require('babel-runtime/core-js/number/max-safe-integer');
|
||||
|
||||
var _maxSafeInteger2 = _interopRequireDefault(_maxSafeInteger);
|
||||
|
||||
var _stringify = require('babel-runtime/core-js/json/stringify');
|
||||
|
||||
var _stringify2 = _interopRequireDefault(_stringify);
|
||||
|
||||
var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck');
|
||||
|
||||
var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
|
||||
|
||||
var _createClass2 = require('babel-runtime/helpers/createClass');
|
||||
|
||||
var _createClass3 = _interopRequireDefault(_createClass2);
|
||||
|
||||
var _typeof2 = require('babel-runtime/helpers/typeof');
|
||||
|
||||
var _typeof3 = _interopRequireDefault(_typeof2);
|
||||
|
||||
var _express = require('express');
|
||||
|
||||
var _express2 = _interopRequireDefault(_express);
|
||||
|
||||
var _mongoose = require('mongoose');
|
||||
|
||||
var _mongoose2 = _interopRequireDefault(_mongoose);
|
||||
|
||||
var _jade = require('jade');
|
||||
|
||||
var _jade2 = _interopRequireDefault(_jade);
|
||||
|
||||
var _basicAuthConnect = require('basic-auth-connect');
|
||||
|
||||
var _basicAuthConnect2 = _interopRequireDefault(_basicAuthConnect);
|
||||
|
||||
var _bodyParser = require('body-parser');
|
||||
|
||||
var _bodyParser2 = _interopRequireDefault(_bodyParser);
|
||||
|
||||
var _JSONStream = require('JSONStream');
|
||||
|
||||
var _JSONStream2 = _interopRequireDefault(_JSONStream);
|
||||
|
||||
var _nwBuilder = require('nw-builder');
|
||||
|
||||
var _nwBuilder2 = _interopRequireDefault(_nwBuilder);
|
||||
|
||||
var _package = require('../../native/package.json');
|
||||
|
||||
var _package2 = _interopRequireDefault(_package);
|
||||
|
||||
var _url = require('url');
|
||||
|
||||
var _MongoDB = require('../writer/MongoDB');
|
||||
|
||||
var _extend = require('extend');
|
||||
|
||||
var _extend2 = _interopRequireDefault(_extend);
|
||||
|
||||
var _fs = require('fs');
|
||||
|
||||
var _fs2 = _interopRequireDefault(_fs);
|
||||
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
|
||||
function getObject(d, def) {
|
||||
if (typeof d === 'undefined' || d === null) {
|
||||
return def || {};
|
||||
} else if ((typeof d === 'undefined' ? 'undefined' : (0, _typeof3.default)(d)) === 'object') {
|
||||
return d;
|
||||
} else {
|
||||
try {
|
||||
return JSON.parse(d);
|
||||
} catch (e) {
|
||||
return def || {};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var viewer = _jade2.default.compileFile(__dirname + '/../../views/viewer.jade');
|
||||
var login = _jade2.default.compileFile(__dirname + '/../../views/login.jade');
|
||||
|
||||
var Viewer = function () {
|
||||
function Viewer(scribe) {
|
||||
(0, _classCallCheck3.default)(this, Viewer);
|
||||
|
||||
this.scribe = scribe;
|
||||
}
|
||||
|
||||
(0, _createClass3.default)(Viewer, [{
|
||||
key: 'getRouter',
|
||||
value: function getRouter() {
|
||||
var scribe = this.scribe;
|
||||
var routerOptions = scribe.module('router/Viewer').options;
|
||||
var clientOptions = scribe.module('router/Viewer/client').options;
|
||||
|
||||
var conn = void 0,
|
||||
Entry = void 0;
|
||||
|
||||
_mongoose2.default.set('debug', routerOptions.debug);
|
||||
conn = _mongoose2.default.createConnection(routerOptions.mongoUri);
|
||||
Entry = conn.model('Entry', _MongoDB.EntrySchema);
|
||||
|
||||
var router = new _express.Router();
|
||||
|
||||
if (routerOptions.username && routerOptions.password) {
|
||||
router.use((0, _basicAuthConnect2.default)(routerOptions.username, routerOptions.password));
|
||||
}
|
||||
|
||||
router.use(_express2.default.static(__dirname + '/../../public'));
|
||||
|
||||
if (routerOptions.useBodyParser) {
|
||||
router.use(_bodyParser2.default.json());
|
||||
}
|
||||
|
||||
var render = function render(req, res) {
|
||||
return res.send(viewer({ config: (0, _stringify2.default)(clientOptions) }));
|
||||
};
|
||||
|
||||
router.get('/', render);
|
||||
router.get('/viewer', render);
|
||||
|
||||
router.post('/rest/timeseries', function (req, res) {
|
||||
try {
|
||||
if (req.body && req.body.date) {
|
||||
req.body.date = req.body.date || {};
|
||||
|
||||
if (req.body.date.$gt) req.body.date.$gt = new Date(req.body.date.$gt);
|
||||
if (req.body.date.$gte) req.body.date.$gte = new Date(req.body.date.$gte);
|
||||
if (req.body.date.$eq) req.body.date.$eq = new Date(req.body.date.$eq);
|
||||
if (req.body.date.$lt) req.body.date.$lt = new Date(req.body.date.$lt);
|
||||
if (req.body.date.$lte) req.body.date.$lte = new Date(req.body.date.$lte);
|
||||
if (req.body._id && Array.isArray(req.body._id.$in)) req.body._id.$in = req.body._id.$in.map(function (a) {
|
||||
return _mongoose2.default.Types.ObjectId(a);
|
||||
});
|
||||
if (req.body._id && typeof req.body._id === 'string') req.body._id = _mongoose2.default.Types.ObjectId(req.body._id);
|
||||
}
|
||||
} catch (e) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
Entry.aggregate([{ $match: req.body }, {
|
||||
$project: {
|
||||
hour: {
|
||||
year: { $year: '$date' },
|
||||
month: { $month: '$date' },
|
||||
day: { $dayOfMonth: '$date' },
|
||||
hour: { $hour: '$date' }
|
||||
},
|
||||
expose: '$expose'
|
||||
}
|
||||
}, {
|
||||
$group: {
|
||||
_id: { hour: '$hour', expose: '$expose' },
|
||||
number: { $sum: 1 }
|
||||
}
|
||||
}], function (err, out) {
|
||||
return res.json(out || []);
|
||||
});
|
||||
});
|
||||
|
||||
router.get('/rest/db/:collection', function (req, res) {
|
||||
var _Entry$find;
|
||||
|
||||
if (!routerOptions.mongoUri) {
|
||||
return res.json({ err: 0, docs: [] });
|
||||
}
|
||||
|
||||
var selector = getObject(req.query.selector);
|
||||
var fields = typeof req.query.fields === 'string' ? req.query.fields : '';
|
||||
var sort = getObject(req.query.sort, { _id: -1 });
|
||||
var limit = !isNaN(req.query.limit) ? Math.max(0, parseInt(req.query.limit)) : _maxSafeInteger2.default;
|
||||
var skip = !isNaN(req.query.skip) ? Math.max(0, parseInt(req.query.skip)) : 0;
|
||||
|
||||
res.type('application/json');
|
||||
|
||||
var stream = (_Entry$find = Entry.find(selector)).select.apply(_Entry$find, (0, _toConsumableArray3.default)(fields.split(' '))).skip(skip).limit(limit).sort(sort).stream();
|
||||
|
||||
stream.pipe(_JSONStream2.default.stringify()).pipe(res);
|
||||
});
|
||||
|
||||
router.delete('/rest/db/:collection', function (req, res) {
|
||||
if (!routerOptions.mongoUri) {
|
||||
res.status(410);
|
||||
return res.send();
|
||||
}
|
||||
|
||||
var ids = req.query.id;
|
||||
|
||||
try {
|
||||
ids = JSON.parse(ids);
|
||||
} catch (e) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
if (!Array.isArray(ids)) {
|
||||
ids = [req.param('id')];
|
||||
}
|
||||
|
||||
Entry.remove({ _id: { $in: ids } }, function (err) {
|
||||
res.status(err ? 410 : 200);
|
||||
res.send();
|
||||
});
|
||||
});
|
||||
|
||||
if (routerOptions.native) {}
|
||||
|
||||
return router;
|
||||
}
|
||||
}, {
|
||||
key: 'getNative',
|
||||
value: function getNative() {
|
||||
var scribe = this.scribe;
|
||||
var nativeOptions = scribe.module('router/Viewer/native').options;
|
||||
|
||||
// update
|
||||
_package2.default.main = (0, _url.format)(nativeOptions);
|
||||
|
||||
// save
|
||||
_fs2.default.writeFileSync(__dirname + '/../../native/package.json', (0, _stringify2.default)(_package2.default, null, 4), { encoding: 'utf8' });
|
||||
|
||||
var nw = new _nwBuilder2.default((0, _extend2.default)(true, {
|
||||
platforms: ['win', 'osx', 'linux'],
|
||||
buildDir: __dirname + '/../../public/native',
|
||||
version: '0.12.3',
|
||||
zip: true
|
||||
}, nativeOptions, { files: __dirname + '/../../native/**/**' }));
|
||||
|
||||
if (nativeOptions.debug) {
|
||||
nw.on('log', function (d) {
|
||||
return console.log(d);
|
||||
});
|
||||
}
|
||||
|
||||
return nw.build();
|
||||
}
|
||||
}]);
|
||||
return Viewer;
|
||||
}();
|
||||
|
||||
exports.default = Viewer;
|
184
dist/routers/viewer.js
vendored
184
dist/routers/viewer.js
vendored
|
@ -1,184 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
|
||||
var _maxSafeInteger = require('babel-runtime/core-js/number/max-safe-integer');
|
||||
|
||||
var _maxSafeInteger2 = _interopRequireDefault(_maxSafeInteger);
|
||||
|
||||
var _stringify = require('babel-runtime/core-js/json/stringify');
|
||||
|
||||
var _stringify2 = _interopRequireDefault(_stringify);
|
||||
|
||||
var _assign = require('babel-runtime/core-js/object/assign');
|
||||
|
||||
var _assign2 = _interopRequireDefault(_assign);
|
||||
|
||||
var _typeof2 = require('babel-runtime/helpers/typeof');
|
||||
|
||||
var _typeof3 = _interopRequireDefault(_typeof2);
|
||||
|
||||
exports.create = create;
|
||||
|
||||
var _express = require('express');
|
||||
|
||||
var _express2 = _interopRequireDefault(_express);
|
||||
|
||||
var _mongoose = require('mongoose');
|
||||
|
||||
var _mongoose2 = _interopRequireDefault(_mongoose);
|
||||
|
||||
var _entry = require('../schemas/entry');
|
||||
|
||||
var _entry2 = _interopRequireDefault(_entry);
|
||||
|
||||
var _jade = require('jade');
|
||||
|
||||
var _jade2 = _interopRequireDefault(_jade);
|
||||
|
||||
var _basicAuth = require('basic-auth');
|
||||
|
||||
var _basicAuth2 = _interopRequireDefault(_basicAuth);
|
||||
|
||||
var _bodyParser = require('body-parser');
|
||||
|
||||
var _bodyParser2 = _interopRequireDefault(_bodyParser);
|
||||
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
|
||||
function getObject(d, def) {
|
||||
if (typeof d === 'undefined' || d === null) {
|
||||
return def || {};
|
||||
} else if ((typeof d === 'undefined' ? 'undefined' : (0, _typeof3.default)(d)) === 'object') {
|
||||
return d;
|
||||
} else {
|
||||
try {
|
||||
return JSON.parse(d);
|
||||
} catch (e) {
|
||||
return def || {};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var viewer = _jade2.default.compileFile(__dirname + '/../../views/viewer.jade');
|
||||
var login = _jade2.default.compileFile(__dirname + '/../../views/login.jade');
|
||||
|
||||
function create() {
|
||||
var mongoUri = arguments.length <= 0 || arguments[0] === undefined ? 'mongodb://localhost/scribe' : arguments[0];
|
||||
var routerConfig = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];
|
||||
var clientConfig = arguments.length <= 2 || arguments[2] === undefined ? {} : arguments[2];
|
||||
var debug = arguments.length <= 3 || arguments[3] === undefined ? false : arguments[3];
|
||||
|
||||
routerConfig = (0, _assign2.default)({
|
||||
useBodyParser: true,
|
||||
username: 'build',
|
||||
password: 'build'
|
||||
}, routerConfig);
|
||||
|
||||
var conn = undefined,
|
||||
Entry = undefined;
|
||||
|
||||
if (mongoUri) {
|
||||
_mongoose2.default.set('debug', debug);
|
||||
conn = _mongoose2.default.createConnection(mongoUri);
|
||||
Entry = conn.model('Entry', _entry2.default);
|
||||
}
|
||||
|
||||
var router = new _express.Router();
|
||||
|
||||
var authenticate = function authenticate(req, res, next) {
|
||||
function unauthorized(res) {
|
||||
res.set('WWW-Authenticate', 'Basic realm=Authorization Required');
|
||||
return res.sendStatus(401);
|
||||
}
|
||||
|
||||
if (routerConfig.authentication === false || !routerConfig.username && !routerConfig.password) {
|
||||
return next();
|
||||
}
|
||||
|
||||
var user = (0, _basicAuth2.default)(req);
|
||||
|
||||
if (!user || !user.name || !user.pass) {
|
||||
return unauthorized(res);
|
||||
}
|
||||
|
||||
if (user.name === routerConfig.username && user.pass === routerConfig.password) {
|
||||
return next();
|
||||
} else {
|
||||
return unauthorized(res);
|
||||
}
|
||||
};
|
||||
|
||||
router.use(authenticate);
|
||||
router.use(_express2.default.static(__dirname + '/../../public'));
|
||||
|
||||
if (routerConfig.useBodyParser) {
|
||||
router.use(_bodyParser2.default.json());
|
||||
}
|
||||
|
||||
var renderViewer = function renderViewer(req, res) {
|
||||
return res.send(viewer({ config: (0, _stringify2.default)(clientConfig) }));
|
||||
};
|
||||
|
||||
router.get('/', renderViewer);
|
||||
router.get('/viewer', renderViewer);
|
||||
|
||||
router.get('/rest/:collection', function (req, res) {
|
||||
if (!mongoUri) {
|
||||
return res.json({ err: 0, docs: [] });
|
||||
}
|
||||
|
||||
var collection = req.params.collection;
|
||||
var selector = getObject(req.query.selector);
|
||||
var fields = typeof req.query.fields === 'string' ? req.query.fields : '';
|
||||
var sort = getObject(req.query.sort, { _id: -1 });
|
||||
var limit = !isNaN(req.query.limit) ? Math.max(0, parseInt(Number(req.query.limit))) : _maxSafeInteger2.default;
|
||||
var col = Entry; // defaulting to Entry for now
|
||||
|
||||
if (!col) {
|
||||
return res.json({ err: 1, docs: [] });
|
||||
}
|
||||
|
||||
col.find(selector).select(fields).sort(sort).limit(limit).lean().exec(function () {
|
||||
var err = arguments.length <= 0 || arguments[0] === undefined ? 0 : arguments[0];
|
||||
var docs = arguments.length <= 1 || arguments[1] === undefined ? [] : arguments[1];
|
||||
return res.json({ err: err, docs: docs });
|
||||
});
|
||||
});
|
||||
|
||||
router.delete('/rest/:collection', function (req, res) {
|
||||
if (!mongoUri) {
|
||||
res.status(410);
|
||||
return res.send();
|
||||
}
|
||||
|
||||
var collection = rreq.params.collection;
|
||||
var ids = req.query.id;
|
||||
|
||||
try {
|
||||
ids = JSON.parse(ids);
|
||||
} catch (e) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
if (!Array.isArray(ids)) {
|
||||
ids = [req.param('id')];
|
||||
}
|
||||
|
||||
var col = Entry; // defaulting to Entry for now
|
||||
|
||||
if (col) {
|
||||
return col.remove({ _id: { $in: ids } }, function (err) {
|
||||
res.status(err ? 410 : 200);
|
||||
res.send();
|
||||
});
|
||||
}
|
||||
|
||||
res.status(410);
|
||||
res.send();
|
||||
});
|
||||
|
||||
return router;
|
||||
}
|
20
dist/schemas/entry.js
vendored
20
dist/schemas/entry.js
vendored
|
@ -1,20 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
|
||||
var _mongoose = require('mongoose');
|
||||
|
||||
var EntrySchema = new _mongoose.Schema({
|
||||
transient: _mongoose.Schema.Types.Object,
|
||||
persistent: _mongoose.Schema.Types.Object,
|
||||
args: _mongoose.Schema.Types.Object,
|
||||
date: { type: Date, default: Date.now },
|
||||
expose: String,
|
||||
serialized: String // experimental
|
||||
});
|
||||
|
||||
EntrySchema.index({ 'persistent.app': 1, 'persistent.id': -1 });
|
||||
|
||||
exports.default = EntrySchema;
|
277
dist/scribe.js
vendored
277
dist/scribe.js
vendored
|
@ -3,90 +3,26 @@
|
|||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.Transform = exports.Reader = exports.Writer = undefined;
|
||||
exports.Writer = exports.Transform = exports.Router = exports.Reader = exports.Middleware = undefined;
|
||||
|
||||
var _stringify = require('babel-runtime/core-js/json/stringify');
|
||||
var _keys = require('babel-runtime/core-js/object/keys');
|
||||
|
||||
var _stringify2 = _interopRequireDefault(_stringify);
|
||||
var _keys2 = _interopRequireDefault(_keys);
|
||||
|
||||
exports.default = function () {
|
||||
var id = arguments.length <= 0 || arguments[0] === undefined ? process.pid : arguments[0];
|
||||
var opts = arguments.length <= 1 || arguments[1] === undefined ? (0, _rc2.default)('scribe', defaultOpts) : arguments[1];
|
||||
var _toConsumableArray2 = require('babel-runtime/helpers/toConsumableArray');
|
||||
|
||||
opts = (0, _extend2.default)(true, {}, defaultOpts, opts);
|
||||
var _toConsumableArray3 = _interopRequireDefault(_toConsumableArray2);
|
||||
|
||||
var console = new _BasicConsole2.default(opts.name, id || opts.instanceId);
|
||||
var _typeof2 = require('babel-runtime/helpers/typeof');
|
||||
|
||||
function appendTransforms(args) {
|
||||
if (opts.mongo && opts.mongoUri && opts.socket && opts.socketPort) {
|
||||
args.push(new _JSON2Converter2.default());
|
||||
args.push(new _MongoDB2.default(opts.mongoUri, opts.debug));
|
||||
args.push(new _SocketIO2.default(opts.socketPort, opts.debug));
|
||||
} else if (opts.mongo && opts.mongoUri) {
|
||||
args.push(new _JSON2Converter2.default());
|
||||
args.push(new _MongoDB2.default(opts.mongoUri, opts.debug));
|
||||
} else if (opts.socket && opts.socketPort) {
|
||||
args.push(new _JSON2Converter2.default());
|
||||
args.push(new _SocketIO2.default(opts.socketPort, opts.debug));
|
||||
}
|
||||
var _typeof3 = _interopRequireDefault(_typeof2);
|
||||
|
||||
return args;
|
||||
}
|
||||
var _getIterator2 = require('babel-runtime/core-js/get-iterator');
|
||||
|
||||
for (var _len = arguments.length, exposers = Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) {
|
||||
exposers[_key - 2] = arguments[_key];
|
||||
}
|
||||
var _getIterator3 = _interopRequireDefault(_getIterator2);
|
||||
|
||||
console.exposed().concat(exposers).forEach(function (expose) {
|
||||
console.expose(expose);
|
||||
|
||||
var args = appendTransforms([expose, 'mongo-socket', new _ErrorExtractor2.default()]);
|
||||
|
||||
console.pipe.apply(console, args);
|
||||
|
||||
console.pipe(expose, 'bash', new _Inspector2.default(opts.inspector), new _DefaultConsole2.default());
|
||||
});
|
||||
|
||||
var args = appendTransforms(['express', 'mongo-socket', new _ErrorExtractor2.default(), new _ExpressExtractor2.default()]);
|
||||
|
||||
console.pipe.apply(console, args);
|
||||
|
||||
console.pipe('express', 'bash', new _ExpressExtractor2.default(), new _ExpressInspector2.default(), new _Inspector2.default(opts.inspector), new _DefaultConsole2.default());
|
||||
|
||||
console.viewer = _viewer.create.bind(null, opts.mongo && opts.mongoUri, opts.web.router, opts.web.client, opts.debug);
|
||||
|
||||
console.build = function () {
|
||||
|
||||
// update
|
||||
_package2.default.main = opts.publicUri + ':' + _path2.default.join(String(opts.web.client.port), opts.basePath);
|
||||
|
||||
// save
|
||||
_fs2.default.writeFileSync(__dirname + '/../native/package.json', (0, _stringify2.default)(_package2.default, null, 4), { encoding: 'utf8' });
|
||||
|
||||
var nw = new _nwBuilder2.default((0, _extend2.default)(true, {
|
||||
platforms: ['win', 'osx', 'linux'],
|
||||
buildDir: __dirname + '/../public/native',
|
||||
version: '0.12.3',
|
||||
zip: true
|
||||
}, opts.nwjs, { files: __dirname + '/../native/**/**' }));
|
||||
|
||||
if (opts.debug) {
|
||||
nw.on('log', function (d) {
|
||||
return console.log(d);
|
||||
});
|
||||
}
|
||||
|
||||
return nw.build();
|
||||
};
|
||||
|
||||
process.on('uncaughtException', function (e) {
|
||||
return console.error(e).then(function () {
|
||||
return process.exit(1);
|
||||
});
|
||||
});
|
||||
|
||||
return console;
|
||||
};
|
||||
exports.resolvePipeline = resolvePipeline;
|
||||
exports.create = create;
|
||||
|
||||
var _fs = require('fs');
|
||||
|
||||
|
@ -96,111 +32,114 @@ var _path = require('path');
|
|||
|
||||
var _path2 = _interopRequireDefault(_path);
|
||||
|
||||
var _Console = require('./readers/Console');
|
||||
|
||||
var _Console2 = _interopRequireDefault(_Console);
|
||||
|
||||
var _BasicConsole = require('./readers/BasicConsole');
|
||||
|
||||
var _BasicConsole2 = _interopRequireDefault(_BasicConsole);
|
||||
|
||||
var _Inspector = require('./transforms/Inspector');
|
||||
|
||||
var _Inspector2 = _interopRequireDefault(_Inspector);
|
||||
|
||||
var _ExpressInspector = require('./transforms/ExpressInspector');
|
||||
|
||||
var _ExpressInspector2 = _interopRequireDefault(_ExpressInspector);
|
||||
|
||||
var _ExpressExtractor = require('./transforms/ExpressExtractor');
|
||||
|
||||
var _ExpressExtractor2 = _interopRequireDefault(_ExpressExtractor);
|
||||
|
||||
var _JSON2Converter = require('./transforms/JSON2Converter');
|
||||
|
||||
var _JSON2Converter2 = _interopRequireDefault(_JSON2Converter);
|
||||
|
||||
var _ErrorExtractor = require('./transforms/ErrorExtractor');
|
||||
|
||||
var _ErrorExtractor2 = _interopRequireDefault(_ErrorExtractor);
|
||||
|
||||
var _MongoDB = require('./writers/MongoDB');
|
||||
|
||||
var _MongoDB2 = _interopRequireDefault(_MongoDB);
|
||||
|
||||
var _SocketIO = require('./writers/SocketIO');
|
||||
|
||||
var _SocketIO2 = _interopRequireDefault(_SocketIO);
|
||||
|
||||
var _DefaultConsole = require('./writers/DefaultConsole');
|
||||
|
||||
var _DefaultConsole2 = _interopRequireDefault(_DefaultConsole);
|
||||
|
||||
var _viewer = require('./routers/viewer');
|
||||
|
||||
var _nwBuilder = require('nw-builder');
|
||||
|
||||
var _nwBuilder2 = _interopRequireDefault(_nwBuilder);
|
||||
|
||||
var _rc = require('rc');
|
||||
|
||||
var _rc2 = _interopRequireDefault(_rc);
|
||||
|
||||
var _package = require('./../native/package.json');
|
||||
|
||||
var _package2 = _interopRequireDefault(_package);
|
||||
|
||||
var _extend = require('extend');
|
||||
|
||||
var _extend2 = _interopRequireDefault(_extend);
|
||||
|
||||
var _middleware = require('./middleware');
|
||||
|
||||
var Middleware = _interopRequireWildcard(_middleware);
|
||||
|
||||
var _reader = require('./reader');
|
||||
|
||||
var Reader = _interopRequireWildcard(_reader);
|
||||
|
||||
var _router = require('./router');
|
||||
|
||||
var Router = _interopRequireWildcard(_router);
|
||||
|
||||
var _transform = require('./transform');
|
||||
|
||||
var Transform = _interopRequireWildcard(_transform);
|
||||
|
||||
var _writer = require('./writer');
|
||||
|
||||
var Writer = _interopRequireWildcard(_writer);
|
||||
|
||||
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }
|
||||
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
|
||||
var Writer = exports.Writer = { MongoDB: _MongoDB2.default, DefaultConsole: _DefaultConsole2.default };
|
||||
var Reader = exports.Reader = { BasicConsole: _BasicConsole2.default, Console: _Console2.default };
|
||||
var Transform = exports.Transform = { Inspector: _Inspector2.default, ExpressInspector: _ExpressInspector2.default, ExpressExtractor: _ExpressExtractor2.default, ErrorExtractor: _ErrorExtractor2.default, JSON2Converter: _JSON2Converter2.default };
|
||||
exports.Middleware = Middleware;
|
||||
exports.Reader = Reader;
|
||||
exports.Router = Router;
|
||||
exports.Transform = Transform;
|
||||
exports.Writer = Writer;
|
||||
function resolvePipeline(scribe, pipeline) {
|
||||
var resolved = [];
|
||||
var _iteratorNormalCompletion = true;
|
||||
var _didIteratorError = false;
|
||||
var _iteratorError = undefined;
|
||||
|
||||
var defaultOpts = {
|
||||
name: 'Scribe',
|
||||
mongoUri: 'mongodb://localhost/scribe',
|
||||
mongo: true,
|
||||
basePath: 'scribe/',
|
||||
socketPort: 4000,
|
||||
socket: true,
|
||||
inspector: {
|
||||
colors: true,
|
||||
showHidden: false,
|
||||
depth: 5,
|
||||
pre: true,
|
||||
callsite: true,
|
||||
tags: true,
|
||||
args: true,
|
||||
metrics: true
|
||||
},
|
||||
web: {
|
||||
router: {
|
||||
username: 'build',
|
||||
password: 'build',
|
||||
authentication: true,
|
||||
sessionSecret: 'scribe-session',
|
||||
useBodyParser: true,
|
||||
useSession: true
|
||||
},
|
||||
client: {
|
||||
socketPorts: [4000],
|
||||
exposed: {
|
||||
all: { label: 'all', query: { expose: { $exists: true } } }
|
||||
try {
|
||||
for (var _iterator = (0, _getIterator3.default)(pipeline), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
|
||||
var through = _step.value;
|
||||
|
||||
if (typeof through === 'function') {
|
||||
resolved.push(new through(scribe));
|
||||
} else if ((typeof through === 'undefined' ? 'undefined' : (0, _typeof3.default)(through)) === 'object') {
|
||||
resolved.push(through);
|
||||
} else if (typeof through === 'string') {
|
||||
var Class = require(_path2.default.join(__dirname, through)).default;
|
||||
resolved.push(new Class(scribe));
|
||||
}
|
||||
}
|
||||
},
|
||||
//error: {label: 'error', query: {expose: 'error'}},
|
||||
//express: {label: 'express', query: {expose: 'express'}},
|
||||
//info: {label: 'info', query: {expose: 'info'}},
|
||||
//log: {label: 'log', query: {expose: 'log'}},
|
||||
//warn: {label: 'warn', query: {expose: 'warn'}},
|
||||
//trace: {label: 'trace', query: {expose: 'trace'}}
|
||||
nwjs: {},
|
||||
debug: false
|
||||
};
|
||||
} catch (err) {
|
||||
_didIteratorError = true;
|
||||
_iteratorError = err;
|
||||
} finally {
|
||||
try {
|
||||
if (!_iteratorNormalCompletion && _iterator.return) {
|
||||
_iterator.return();
|
||||
}
|
||||
} finally {
|
||||
if (_didIteratorError) {
|
||||
throw _iteratorError;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
;
|
||||
return resolved;
|
||||
}
|
||||
|
||||
function create(opts) {
|
||||
opts = (0, _extend2.default)(true, (0, _rc2.default)('scribe', {}), opts);
|
||||
|
||||
// create default console
|
||||
var console = new Reader.BasicConsole(opts);
|
||||
var _opts = opts;
|
||||
var exposeMap = _opts.expose;
|
||||
var pipelineMap = _opts['expose/pipeline'];
|
||||
|
||||
|
||||
[].concat((0, _toConsumableArray3.default)(console.exposed()), (0, _toConsumableArray3.default)((0, _keys2.default)(exposeMap))).forEach(function (expose) {
|
||||
if (expose === 'default') return;
|
||||
var pipelines = exposeMap[expose] || exposeMap.default;
|
||||
if (Array.isArray(pipelines)) {
|
||||
pipelines.forEach(function (pipeline) {
|
||||
if (Array.isArray(pipelineMap[pipeline])) {
|
||||
if (opts.debug) {
|
||||
process.stdout.write('Exposing ' + expose + ' through ' + pipeline + '\n');
|
||||
}
|
||||
|
||||
console.expose(expose);
|
||||
console.pipe.apply(console, [expose, pipeline].concat((0, _toConsumableArray3.default)(resolvePipeline(console, pipelineMap[pipeline]))));
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
if (opts.handleUncaughtException) {
|
||||
process.on('uncaughtException', function (e) {
|
||||
return console.error(e).then(function () {
|
||||
return process.exit(1);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return console;
|
||||
}
|
||||
|
|
|
@ -27,7 +27,7 @@ var ErrorExtractor = function () {
|
|||
key: "through",
|
||||
value: function through(data, callback) {
|
||||
data.args = data.args.map(function (a) {
|
||||
return a instanceof Error ? JSON.parse((0, _stringify2.default)(a, ["message", "arguments", "type", "name", "stack"])) : a;
|
||||
return a instanceof Error ? a.toJSON ? a.toJSON() : JSON.parse((0, _stringify2.default)(a, ["message", "arguments", "type", "name", "stack", "code"])) : a;
|
||||
});
|
||||
|
||||
callback(null, data);
|
|
@ -30,7 +30,7 @@ var ExpressExtractor = function () {
|
|||
(0, _createClass3.default)(ExpressExtractor, [{
|
||||
key: 'ip',
|
||||
value: function ip(req) {
|
||||
return req.ip || req._remoteAddress || req.remoteAddress || req.connection && req.connection.remoteAddress || req.headers['x-forwarded-for'] || undefined;
|
||||
return req.ip || req._remoteAddress || req.remoteAddress || req.connection && req.connection.remoteAddress || req.headers['x-forwarded-for'] || 'xx.x.x.xx';
|
||||
}
|
||||
}, {
|
||||
key: 'header',
|
|
@ -48,7 +48,7 @@ var ExpressInspector = function () {
|
|||
symbol = '';
|
||||
}
|
||||
|
||||
var ret = undefined;
|
||||
var ret = void 0;
|
||||
if (this.symbols) {
|
||||
ret = symbol + ' ' + stat;
|
||||
} else {
|
||||
|
@ -63,7 +63,7 @@ var ExpressInspector = function () {
|
|||
var express = data.args[0];
|
||||
|
||||
data.transient['callsite'] = '' + express.ip;
|
||||
data.args = [_chalk2.default.bgGreen.black(' ' + express.method + ' ') + ' ' + _chalk2.default.gray(express.url) + ' ' + this.status(express) + ' - ' + express.contentLength + ' ' + _chalk2.default.gray('(' + express.duration.toFixed(3) + 'ms)')];
|
||||
data.args = [_chalk2.default.bgGreen.black(' ' + express.method + ' ') + ' ' + _chalk2.default.gray(express.originalUrl) + ' ' + this.status(express) + ' - ' + express.contentLength + ' ' + _chalk2.default.gray('(' + express.duration.toFixed(3) + 'ms)')];
|
||||
|
||||
callback(null, data);
|
||||
}
|
38
dist/transform/FullTextSerialize.js
vendored
Normal file
38
dist/transform/FullTextSerialize.js
vendored
Normal file
|
@ -0,0 +1,38 @@
|
|||
'use strict';
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
|
||||
var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck');
|
||||
|
||||
var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
|
||||
|
||||
var _createClass2 = require('babel-runtime/helpers/createClass');
|
||||
|
||||
var _createClass3 = _interopRequireDefault(_createClass2);
|
||||
|
||||
var _JSON = require('../libs/JSON2');
|
||||
|
||||
var JSON2 = _interopRequireWildcard(_JSON);
|
||||
|
||||
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }
|
||||
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
|
||||
var FullTextSerialize = function () {
|
||||
function FullTextSerialize() {
|
||||
(0, _classCallCheck3.default)(this, FullTextSerialize);
|
||||
}
|
||||
|
||||
(0, _createClass3.default)(FullTextSerialize, [{
|
||||
key: 'through',
|
||||
value: function through(data, callback) {
|
||||
data.serialized = JSON2.stringify(data);
|
||||
callback(null, data);
|
||||
}
|
||||
}]);
|
||||
return FullTextSerialize;
|
||||
}();
|
||||
|
||||
exports.default = FullTextSerialize;
|
|
@ -39,12 +39,10 @@ function inspect(x, ctx) {
|
|||
}
|
||||
|
||||
var Inspector = function () {
|
||||
function Inspector() {
|
||||
var inspectOpts = arguments.length <= 0 || arguments[0] === undefined ? { colors: true, showHidden: false,
|
||||
depth: 5, pre: true, args: true, metrics: true, tags: true } : arguments[0];
|
||||
function Inspector(scribe) {
|
||||
(0, _classCallCheck3.default)(this, Inspector);
|
||||
|
||||
this.inspectOpts = inspectOpts;
|
||||
this.options = scribe.module('transform/Inspector').options;
|
||||
}
|
||||
|
||||
(0, _createClass3.default)(Inspector, [{
|
||||
|
@ -54,7 +52,7 @@ var Inspector = function () {
|
|||
args[_key - 1] = arguments[_key];
|
||||
}
|
||||
|
||||
var ctx = this.inspectOpts;
|
||||
var ctx = this.options;
|
||||
|
||||
if (typeof f !== 'string') {
|
||||
var objects = [];
|
||||
|
@ -156,11 +154,11 @@ var Inspector = function () {
|
|||
data.args = '';
|
||||
}
|
||||
|
||||
var pre = this.inspectOpts.pre ? this.inspectPre(data) + ' ' : '';
|
||||
var tags = this.inspectOpts.tags ? this.inspectTags(data) : '';
|
||||
var metrics = this.inspectOpts.metrics ? this.inspectMetrics(data) : '';
|
||||
var site = this.inspectOpts.callsite ? this.inspectCallSite(data) : '';
|
||||
var pretty = this.inspectOpts.args ? this.inspectArguments(data) : '';
|
||||
var pre = this.options.pre ? this.inspectPre(data) + ' ' : '';
|
||||
var tags = this.options.tags ? this.inspectTags(data) : '';
|
||||
var metrics = this.options.metrics ? this.inspectMetrics(data) : '';
|
||||
var site = this.options.callsite ? this.inspectCallSite(data) : '';
|
||||
var pretty = this.options.args ? this.inspectArguments(data) : '';
|
||||
var inspected = pretty.split('\n').map(function (line) {
|
||||
return pre + ' ' + [tags, metrics, line, site].join(' ');
|
||||
});
|
|
@ -20,18 +20,18 @@ function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj;
|
|||
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
|
||||
var JSON2Converter = function () {
|
||||
function JSON2Converter() {
|
||||
(0, _classCallCheck3.default)(this, JSON2Converter);
|
||||
var ToJSON2 = function () {
|
||||
function ToJSON2() {
|
||||
(0, _classCallCheck3.default)(this, ToJSON2);
|
||||
}
|
||||
|
||||
(0, _createClass3.default)(JSON2Converter, [{
|
||||
(0, _createClass3.default)(ToJSON2, [{
|
||||
key: 'through',
|
||||
value: function through(data, callback) {
|
||||
callback(null, JSON.parse(JSON2.stringify(data)));
|
||||
}
|
||||
}]);
|
||||
return JSON2Converter;
|
||||
return ToJSON2;
|
||||
}();
|
||||
|
||||
exports.default = JSON2Converter;
|
||||
exports.default = ToJSON2;
|
52
dist/transform/index.js
vendored
Normal file
52
dist/transform/index.js
vendored
Normal file
|
@ -0,0 +1,52 @@
|
|||
'use strict';
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
|
||||
var _ErrorExtractor = require('./ErrorExtractor');
|
||||
|
||||
Object.defineProperty(exports, 'ErrorExtractor', {
|
||||
enumerable: true,
|
||||
get: function get() {
|
||||
return _interopRequireDefault(_ErrorExtractor).default;
|
||||
}
|
||||
});
|
||||
|
||||
var _ExpressExtractor = require('./ExpressExtractor');
|
||||
|
||||
Object.defineProperty(exports, 'ExpressExtractor', {
|
||||
enumerable: true,
|
||||
get: function get() {
|
||||
return _interopRequireDefault(_ExpressExtractor).default;
|
||||
}
|
||||
});
|
||||
|
||||
var _ExpressInspector = require('./ExpressInspector');
|
||||
|
||||
Object.defineProperty(exports, 'ExpressInspector', {
|
||||
enumerable: true,
|
||||
get: function get() {
|
||||
return _interopRequireDefault(_ExpressInspector).default;
|
||||
}
|
||||
});
|
||||
|
||||
var _Inspector = require('./Inspector');
|
||||
|
||||
Object.defineProperty(exports, 'Inspector', {
|
||||
enumerable: true,
|
||||
get: function get() {
|
||||
return _interopRequireDefault(_Inspector).default;
|
||||
}
|
||||
});
|
||||
|
||||
var _ToJSON = require('./ToJSON2');
|
||||
|
||||
Object.defineProperty(exports, 'ToJSON2', {
|
||||
enumerable: true,
|
||||
get: function get() {
|
||||
return _interopRequireDefault(_ToJSON).default;
|
||||
}
|
||||
});
|
||||
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
49
dist/writers/MongoDB.js → dist/writer/MongoDB.js
vendored
49
dist/writers/MongoDB.js → dist/writer/MongoDB.js
vendored
|
@ -3,6 +3,7 @@
|
|||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.EntrySchema = undefined;
|
||||
|
||||
var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck');
|
||||
|
||||
|
@ -16,29 +17,55 @@ var _mongoose = require('mongoose');
|
|||
|
||||
var _mongoose2 = _interopRequireDefault(_mongoose);
|
||||
|
||||
var _entry = require('../schemas/entry');
|
||||
|
||||
var _entry2 = _interopRequireDefault(_entry);
|
||||
|
||||
var _JSON = require('../libs/JSON2');
|
||||
|
||||
var JSON2 = _interopRequireWildcard(_JSON);
|
||||
|
||||
var _express = require('express');
|
||||
|
||||
var _express2 = _interopRequireDefault(_express);
|
||||
|
||||
var _jade = require('jade');
|
||||
|
||||
var _jade2 = _interopRequireDefault(_jade);
|
||||
|
||||
var _basicAuthConnect = require('basic-auth-connect');
|
||||
|
||||
var _basicAuthConnect2 = _interopRequireDefault(_basicAuthConnect);
|
||||
|
||||
var _bodyParser = require('body-parser');
|
||||
|
||||
var _bodyParser2 = _interopRequireDefault(_bodyParser);
|
||||
|
||||
var _JSONStream = require('JSONStream');
|
||||
|
||||
var _JSONStream2 = _interopRequireDefault(_JSONStream);
|
||||
|
||||
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }
|
||||
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
|
||||
var EntrySchema = exports.EntrySchema = new _mongoose.Schema({
|
||||
transient: _mongoose.Schema.Types.Object,
|
||||
persistent: _mongoose.Schema.Types.Object,
|
||||
args: _mongoose.Schema.Types.Object,
|
||||
date: { type: Date, default: Date.now },
|
||||
expose: String,
|
||||
serialized: String
|
||||
});
|
||||
|
||||
EntrySchema.index({ 'persistent.app': 1, 'persistent.id': -1 });
|
||||
EntrySchema.index({ 'serialized': 1, 'comment': 'text' });
|
||||
|
||||
var MongoDB = function () {
|
||||
function MongoDB() {
|
||||
var address = arguments.length <= 0 || arguments[0] === undefined ? 'mongodb://localhost/scribe' : arguments[0];
|
||||
var debug = arguments.length <= 1 || arguments[1] === undefined ? false : arguments[1];
|
||||
function MongoDB(scribe) {
|
||||
(0, _classCallCheck3.default)(this, MongoDB);
|
||||
|
||||
_mongoose2.default.set('debug', debug);
|
||||
this.options = scribe.module('writer/MongoDB').options;
|
||||
|
||||
var conn = _mongoose2.default.createConnection(address);
|
||||
this._debug = debug;
|
||||
this._entry = conn.model('Entry', _entry2.default);
|
||||
var conn = _mongoose2.default.createConnection(this.options.uri);
|
||||
this._debug = this.options.debug;
|
||||
this._entry = conn.model('Entry', EntrySchema);
|
||||
}
|
||||
|
||||
(0, _createClass3.default)(MongoDB, [{
|
|
@ -18,18 +18,16 @@ var _socket2 = _interopRequireDefault(_socket);
|
|||
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
|
||||
var sio = undefined;
|
||||
var sio = void 0;
|
||||
|
||||
var SocketIO = function () {
|
||||
function SocketIO() {
|
||||
var port = arguments.length <= 0 || arguments[0] === undefined ? 4000 : arguments[0];
|
||||
var debug = arguments.length <= 1 || arguments[1] === undefined ? false : arguments[1];
|
||||
function SocketIO(scribe) {
|
||||
(0, _classCallCheck3.default)(this, SocketIO);
|
||||
|
||||
process.env.DEBUG = debug && 'socket.io*';
|
||||
this.options = scribe.module('writer/SocketIO').options;
|
||||
|
||||
if (!sio) {
|
||||
sio = (0, _socket2.default)(port);
|
||||
sio = (0, _socket2.default)(this.options.port, this.options.options);
|
||||
}
|
||||
}
|
||||
|
34
dist/writer/index.js
vendored
Normal file
34
dist/writer/index.js
vendored
Normal file
|
@ -0,0 +1,34 @@
|
|||
'use strict';
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
|
||||
var _DefaultConsole = require('./DefaultConsole');
|
||||
|
||||
Object.defineProperty(exports, 'DefaultConsole', {
|
||||
enumerable: true,
|
||||
get: function get() {
|
||||
return _interopRequireDefault(_DefaultConsole).default;
|
||||
}
|
||||
});
|
||||
|
||||
var _MongoDB = require('./MongoDB');
|
||||
|
||||
Object.defineProperty(exports, 'MongoDB', {
|
||||
enumerable: true,
|
||||
get: function get() {
|
||||
return _interopRequireDefault(_MongoDB).default;
|
||||
}
|
||||
});
|
||||
|
||||
var _SocketIO = require('./SocketIO');
|
||||
|
||||
Object.defineProperty(exports, 'SocketIO', {
|
||||
enumerable: true,
|
||||
get: function get() {
|
||||
return _interopRequireDefault(_SocketIO).default;
|
||||
}
|
||||
});
|
||||
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
@ -1,7 +1,7 @@
|
|||
import cluster from 'cluster'
|
||||
import http from 'http'
|
||||
import express from 'express'
|
||||
import Scribe from '../index.js'
|
||||
import * as Scribe from '../index.js';
|
||||
import * as JSON2 from '../src/libs/JSON2'
|
||||
|
||||
const port = 4005;
|
||||
|
@ -13,40 +13,23 @@ if (cluster.isMaster) {
|
|||
cluster.fork();
|
||||
}
|
||||
} else {
|
||||
const console = new Scribe(cluster.worker.id, {
|
||||
name: 'ScribeCluster',
|
||||
mongoUri: 'mongodb://localhost/scribe',
|
||||
publicUri: 'http://localhost',
|
||||
basePath: 'scribe/',
|
||||
socketPort: socketPort + cluster.worker.id - 1, // assign a port to a worker worker
|
||||
web: {
|
||||
router: {
|
||||
username: 'build',
|
||||
password: 'build',
|
||||
// authentication must be implemented by yourself
|
||||
// if you are in cluster mode
|
||||
authentication: false,
|
||||
sessionSecret: 'scribe-session',
|
||||
useBodyParser: true,
|
||||
useSession: true
|
||||
const options = {
|
||||
"app": 'cluster-server',
|
||||
"id": process.pid,
|
||||
"module": {
|
||||
"writer/SocketIO": {
|
||||
"port": socketPort + cluster.worker.id - 1,
|
||||
"options": {}
|
||||
},
|
||||
client: {
|
||||
port: port,
|
||||
socketPorts: [socketPort, socketPort + 1, socketPort + 2, socketPort + 3],
|
||||
exposed: {
|
||||
all: {label: 'all', query: {expose: {$exists: true}}},
|
||||
error: {label: 'error', query: {expose: 'error'}},
|
||||
express: {label: 'express', query: {expose: 'express'}},
|
||||
info: {label: 'info', query: {expose: 'info'}},
|
||||
log: {label: 'log', query: {expose: 'log'}},
|
||||
warn: {label: 'warn', query: {expose: 'warn'}},
|
||||
trace: {label: 'trace', query: {expose: 'trace'}},
|
||||
perf: {label: 'perf', query: {expose: 'perf'}}
|
||||
}
|
||||
"router/Viewer/client": {
|
||||
"background": "#131B21",
|
||||
"socketPorts": [socketPort, socketPort + 1, socketPort + 2, socketPort + 3]
|
||||
}
|
||||
},
|
||||
debug: false
|
||||
});
|
||||
"debug": false
|
||||
};
|
||||
|
||||
const console = Scribe.create(options);
|
||||
|
||||
// default tags
|
||||
console.persistent('tags', ['mocha', 'scribe']);
|
||||
|
@ -61,9 +44,14 @@ if (cluster.isMaster) {
|
|||
|
||||
// express
|
||||
const app = express();
|
||||
const logger = new Scribe.Middleware.ExpressLogger(console);
|
||||
const viewer = new Scribe.Router.Viewer(console);
|
||||
|
||||
// express logger
|
||||
app.use(console.middleware('express'));
|
||||
app.use(logger.getMiddleware());
|
||||
|
||||
// viewer
|
||||
app.use('/scribe', viewer.getRouter());
|
||||
|
||||
// test harness
|
||||
app.get('/test', (req, res) => {
|
||||
|
@ -72,16 +60,8 @@ if (cluster.isMaster) {
|
|||
});
|
||||
});
|
||||
|
||||
// viewer
|
||||
app.use('/scribe', console.viewer());
|
||||
|
||||
app.listen(port, () => console.log(`Listening to ${port} - Cluster ${cluster.worker.id}`));
|
||||
|
||||
// build native app
|
||||
if (cluster.worker.id == 1) {
|
||||
console.build().then(()=> console.log('Created native apps!')).catch(err => console.error(err));
|
||||
}
|
||||
|
||||
// override default console
|
||||
console.override();
|
||||
|
||||
|
|
|
@ -1,73 +1,70 @@
|
|||
import express from 'express'
|
||||
import Scribe from '../index.js'
|
||||
import * as Scribe from '../index.js';
|
||||
import * as JSON2 from '../src/libs/JSON2'
|
||||
|
||||
const port = 4005;
|
||||
const socketPort = 3000;
|
||||
|
||||
const console = new Scribe(process.pid, {
|
||||
name: 'Scribe',
|
||||
mongoUri: 'mongodb://localhost/scribe',
|
||||
mongo: false,
|
||||
publicUri: 'http://localhost',
|
||||
basePath: 'scribe/',
|
||||
socketPort: socketPort,
|
||||
web: {
|
||||
router: {
|
||||
username: 'build',
|
||||
password: 'build',
|
||||
authentication: true,
|
||||
sessionSecret: 'scribe-session',
|
||||
useBodyParser: true,
|
||||
useSession: true
|
||||
const options = {
|
||||
"app": 'simple-server-stream-only',
|
||||
"id": process.pid,
|
||||
"expose": {
|
||||
"default": [
|
||||
"socket",
|
||||
"bash"
|
||||
],
|
||||
"express": [
|
||||
"express-socket",
|
||||
"express-bash"
|
||||
]
|
||||
},
|
||||
"expose/pipeline": {
|
||||
"socket": [
|
||||
"transform/ErrorExtractor",
|
||||
"transform/ToJSON2",
|
||||
"transform/FullTextSerialize",
|
||||
"writer/SocketIO"
|
||||
],
|
||||
"express-socket": [
|
||||
"transform/ExpressExtractor",
|
||||
"transform/ErrorExtractor",
|
||||
"transform/ToJSON2",
|
||||
"transform/FullTextSerialize",
|
||||
"writer/SocketIO"
|
||||
]
|
||||
},
|
||||
"module": {
|
||||
"writer/SocketIO": {
|
||||
"port": socketPort,
|
||||
"options": {}
|
||||
},
|
||||
client: {
|
||||
port: port,
|
||||
socketPorts: [socketPort],
|
||||
exposed: {
|
||||
all: {label: 'all', query: {expose: {$exists: true}}},
|
||||
error: {label: 'error', query: {expose: 'error'}},
|
||||
express: {label: 'express', query: {expose: 'express'}},
|
||||
info: {label: 'info', query: {expose: 'info'}},
|
||||
log: {label: 'log', query: {expose: 'log'}},
|
||||
warn: {label: 'warn', query: {expose: 'warn'}},
|
||||
trace: {label: 'trace', query: {expose: 'trace'}},
|
||||
timing: {label: 'time', query: {expose: 'timing'}},
|
||||
user: {label: 'user', query: {'transient.tags': {$in: ['USER ID']}}}
|
||||
}
|
||||
"router/Viewer/client": {
|
||||
"background": "#131B21",
|
||||
"socketPorts": [
|
||||
socketPort
|
||||
]
|
||||
}
|
||||
},
|
||||
native: {},
|
||||
debug: false
|
||||
});
|
||||
"debug": false
|
||||
};
|
||||
|
||||
const console = Scribe.create(options);
|
||||
|
||||
console.time('serverStartup');
|
||||
|
||||
// default tags
|
||||
console.persistent('tags', ['mocha', 'scribe']);
|
||||
|
||||
// modify an existing pipeline i.e. the express
|
||||
console.pipe('express', 'mongo-socket').unshift({
|
||||
through(data, callback){
|
||||
const {req, res} = data.args[0]; // access the req, res objects
|
||||
|
||||
// modify data as needed
|
||||
|
||||
// i.e.
|
||||
// add user tags
|
||||
const tags = data.transient.tags || [];
|
||||
tags.push('USER_ID'); // perhaps put in the user id here
|
||||
data.transient.tags = tags;
|
||||
|
||||
callback(null, data);
|
||||
}
|
||||
});
|
||||
|
||||
// express
|
||||
const app = express();
|
||||
const logger = new Scribe.Middleware.ExpressLogger(console);
|
||||
const viewer = new Scribe.Router.Viewer(console);
|
||||
|
||||
// express logger
|
||||
app.use(console.middleware('express'));
|
||||
app.use(logger.getMiddleware());
|
||||
|
||||
// viewer
|
||||
app.use('/scribe', viewer.getRouter());
|
||||
|
||||
// test harness
|
||||
app.get('/test', (req, res) => {
|
||||
|
@ -76,9 +73,6 @@ app.get('/test', (req, res) => {
|
|||
});
|
||||
});
|
||||
|
||||
// viewer
|
||||
app.use('/scribe', console.viewer());
|
||||
|
||||
app.listen(port, () => {
|
||||
console.log(`Listening to ${port}`);
|
||||
|
||||
|
@ -97,6 +91,3 @@ app.listen(port, () => {
|
|||
|
||||
console.timeEnd('serverStartup');
|
||||
});
|
||||
|
||||
// build native app
|
||||
console.build().then(()=> console.log('Created native apps!')).catch(err => console.error(err));
|
|
@ -1,52 +1,34 @@
|
|||
import express from 'express'
|
||||
import Scribe from '../index.js'
|
||||
import * as JSON2 from '../src/libs/JSON2'
|
||||
import express from 'express';
|
||||
import * as Scribe from '../index.js';
|
||||
import * as JSON2 from '../src/libs/JSON2';
|
||||
|
||||
const port = 4005;
|
||||
const socketPort = 50000;
|
||||
|
||||
const console = new Scribe(process.pid, {
|
||||
name: 'Scribe',
|
||||
mongoUri: 'mongodb://localhost/scribe',
|
||||
publicUri: 'http://localhost',
|
||||
basePath: 'scribe/',
|
||||
socketPort: socketPort,
|
||||
web: {
|
||||
router: {
|
||||
username: 'build',
|
||||
password: 'build',
|
||||
authentication: true,
|
||||
sessionSecret: 'scribe-session',
|
||||
useBodyParser: true,
|
||||
useSession: true
|
||||
const options = {
|
||||
"app": 'simple-server',
|
||||
"id": process.pid,
|
||||
"module": {
|
||||
"writer/SocketIO": {
|
||||
"port": 50000,
|
||||
"options": {}
|
||||
},
|
||||
client: {
|
||||
port: port,
|
||||
socketPorts: [socketPort],
|
||||
exposed: {
|
||||
all: {label: 'all', query: {expose: {$exists: true}}},
|
||||
error: {label: 'error', query: {expose: 'error'}},
|
||||
express: {label: 'express', query: {expose: 'express'}},
|
||||
info: {label: 'info', query: {expose: 'info'}},
|
||||
log: {label: 'log', query: {expose: 'log'}},
|
||||
warn: {label: 'warn', query: {expose: 'warn'}},
|
||||
trace: {label: 'trace', query: {expose: 'trace'}},
|
||||
timing: {label: 'time', query: {expose: 'timing'}},
|
||||
user: {label: 'user', query: {'transient.tags': {$in: ['USER ID']}}}
|
||||
}
|
||||
"router/Viewer/client": {
|
||||
"background": "#131B21",
|
||||
"socketPorts": [
|
||||
50000
|
||||
]
|
||||
}
|
||||
},
|
||||
native: {},
|
||||
debug: false
|
||||
});
|
||||
"debug": false
|
||||
};
|
||||
|
||||
const console = Scribe.create(options);
|
||||
|
||||
console.time('serverStartup');
|
||||
|
||||
// default tags
|
||||
console.persistent('tags', ['mocha', 'scribe']);
|
||||
|
||||
// modify an existing pipeline i.e. the express
|
||||
console.pipe('express', 'mongo-socket').unshift({
|
||||
var through = {
|
||||
through(data, callback){
|
||||
const {req, res} = data.args[0]; // access the req, res objects
|
||||
|
||||
|
@ -60,13 +42,22 @@ console.pipe('express', 'mongo-socket').unshift({
|
|||
|
||||
callback(null, data);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// modify an existing pipeline i.e. the express
|
||||
console.pipe('express', 'express-mongo-socket')
|
||||
.unshift(through);
|
||||
|
||||
// express
|
||||
const app = express();
|
||||
const logger = new Scribe.Middleware.ExpressLogger(console);
|
||||
const viewer = new Scribe.Router.Viewer(console);
|
||||
|
||||
// express logger
|
||||
app.use(console.middleware('express'));
|
||||
app.use(logger.getMiddleware());
|
||||
|
||||
// viewer
|
||||
app.use('/scribe', viewer.getRouter());
|
||||
|
||||
// test harness
|
||||
app.get('/test', (req, res) => {
|
||||
|
@ -75,9 +66,6 @@ app.get('/test', (req, res) => {
|
|||
});
|
||||
});
|
||||
|
||||
// viewer
|
||||
app.use('/scribe', console.viewer());
|
||||
|
||||
app.listen(port, () => {
|
||||
console.log(`Listening to ${port}`);
|
||||
|
||||
|
@ -103,4 +91,6 @@ app.listen(port, () => {
|
|||
});
|
||||
|
||||
// build native app
|
||||
console.build().then(()=> console.log('Created native apps!')).catch(err => console.error(err));
|
||||
viewer.getNative().then(()=> console.log('Created native apps!')).catch(err => console.error(err));
|
||||
|
||||
setInterval(() => console.log(`Heartbeat - ${Date.now()} ... every 5 seconds`), 5000);
|
140
herokuapp/app.js
140
herokuapp/app.js
|
@ -1,88 +1,84 @@
|
|||
/* jshint -W079 */
|
||||
(function() {
|
||||
var scribe = require('../src/scribe')(),
|
||||
console = process.console,
|
||||
express = require('express'),
|
||||
path = require('path'),
|
||||
app = express();
|
||||
(function () {
|
||||
var Scribe = require('../');
|
||||
var express = require('express');
|
||||
var path = require('path');
|
||||
var bodyParser = require('body-parser');
|
||||
var app = express();
|
||||
|
||||
console.addLogger('log', 'green');
|
||||
console.addLogger('err', 'red');
|
||||
const options = {
|
||||
"app": 'simple-server',
|
||||
"id": process.pid,
|
||||
"module": {
|
||||
"writer/MongoDB": {
|
||||
"uri": process.env.MONGOLAB_URI || "mongodb://localhost/scribe"
|
||||
},
|
||||
"router/Viewer": {
|
||||
"mongoUri": process.env.MONGOLAB_URI || "mongodb://localhost/scribe"
|
||||
},
|
||||
"writer/SocketIO": {
|
||||
"port": 50000,
|
||||
"options": {}
|
||||
},
|
||||
"router/Viewer/client": {
|
||||
"background": "#222",
|
||||
"socketPorts": [
|
||||
50000
|
||||
]
|
||||
}
|
||||
},
|
||||
"debug": false
|
||||
};
|
||||
|
||||
// port
|
||||
app.set('port', (process.env.PORT || 5000));
|
||||
const console = Scribe.create(options);
|
||||
const logger = new Scribe.Middleware.ExpressLogger(console);
|
||||
const viewer = new Scribe.Router.Viewer(console);
|
||||
|
||||
// public dir
|
||||
app.use('/', express.static(path.join(__dirname, 'public')));
|
||||
console.persistent('tags', ['scribe']);
|
||||
|
||||
// scribe
|
||||
app.use(scribe.express.logger());
|
||||
app.use('/logs', scribe.webPanel());
|
||||
app.set('port', (process.env.PORT || 5000));
|
||||
|
||||
// index
|
||||
app.get('/', function(req, res) {
|
||||
res.sendFile(path.join(__dirname, 'views', 'index.html'));
|
||||
});
|
||||
app.use(logger.getMiddleware());
|
||||
app.use(bodyParser.urlencoded({extended: true}));
|
||||
app.use(bodyParser.json());
|
||||
|
||||
function _param(name) {
|
||||
name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
|
||||
var regex = new RegExp("[\\?&]" + name + "=([^&#]*)");
|
||||
var results = regex.exec("https://r.com?" + this.body);
|
||||
return results === null ? "" : decodeURIComponent(results[1].replace(/\+/g, " "));
|
||||
app.use('/', express.static(path.join(__dirname, 'public')));
|
||||
|
||||
app.use('/scribe', viewer.getRouter());
|
||||
|
||||
app.post('/', function (req, res) {
|
||||
var tag = req.param('tag');
|
||||
var msg = req.param('msg');
|
||||
|
||||
if (!msg) {
|
||||
return res.status(400).send('Param `msg` not defined');
|
||||
}
|
||||
|
||||
// log
|
||||
app.post('/', function(req, res) {
|
||||
try {
|
||||
msg = JSON.parse(msg);
|
||||
} catch (e) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
req.body = '';
|
||||
req.setEncoding('utf8');
|
||||
// print
|
||||
if (tag) {
|
||||
console.tag(tag).log(msg);
|
||||
} else {
|
||||
console.log(msg);
|
||||
}
|
||||
|
||||
req.on('data', function(chunk) {
|
||||
req.body += chunk;
|
||||
});
|
||||
res.status(200).send("Success! Check system logs to see your message.");
|
||||
});
|
||||
|
||||
req.on('end', function() {
|
||||
req.param = _param;
|
||||
var port = app.get('port');
|
||||
|
||||
var tag = req.param('tag');
|
||||
var msg = req.param('msg');
|
||||
//log something every 10 minutes
|
||||
setInterval(function () {
|
||||
console.tag("Test").log("Hi there ! Server date : " + new Date());
|
||||
}, 10 * 60 * 1000);
|
||||
|
||||
if (!msg) {
|
||||
return res.status(400).send('Param `msg` not defined');
|
||||
}
|
||||
|
||||
try {
|
||||
msg = JSON.parse(msg);
|
||||
} catch (e) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
// print
|
||||
if (tag) {
|
||||
console.tag(tag).log(msg);
|
||||
} else {
|
||||
console.log(msg);
|
||||
}
|
||||
|
||||
res.status(200).send("Success! Check system logs to see your message.");
|
||||
});
|
||||
|
||||
req.on('close', function(err){
|
||||
console.err(err);
|
||||
res.status(400).json("Error! Check system logs to identify the error.");
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
var port = app.get('port');
|
||||
|
||||
//log something every 10 minutes
|
||||
setInterval(function () {
|
||||
console.tag("Test").log("Hi there ! Server date : " + new Date());
|
||||
}, 10 * 60 * 1000);
|
||||
|
||||
app.listen(port, function() {
|
||||
console.time().log('Server listening at port ' + port);
|
||||
});
|
||||
app.listen(port, function () {
|
||||
console.time().log('Server listening at port ' + port);
|
||||
});
|
||||
|
||||
})();
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 170 KiB After Width: | Height: | Size: 228 KiB |
122
herokuapp/public/index.html
Normal file
122
herokuapp/public/index.html
Normal file
|
@ -0,0 +1,122 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<base target="_frame"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>ScribeJS Demo</title>
|
||||
<link href='https://fonts.googleapis.com/css?family=Open+Sans:400,300,600,700' rel='stylesheet' type='text/css'>
|
||||
<link href='https://fonts.googleapis.com/css?family=Ubuntu+Mono:400,700' rel='stylesheet' type='text/css'>
|
||||
<link href="css/style.css" rel="stylesheet" type="text/css">
|
||||
<script src="https://code.jquery.com/jquery-2.2.3.min.js"
|
||||
integrity="sha256-a23g1Nt4dtEYOj7bR+vTu7+T8VP13humZFBJNIYoEJo=" crossorigin="anonymous"></script>
|
||||
<script src="js/index.js" type="text/javascript"></script>
|
||||
</head>
|
||||
<body>
|
||||
<style>#forkongithub a {
|
||||
background: #000;
|
||||
color: #fff;
|
||||
text-decoration: none;
|
||||
font-family: arial, sans-serif;
|
||||
text-align: center;
|
||||
box-sizing: content-box;
|
||||
font-weight: bold;
|
||||
padding: 5px 40px;
|
||||
font-size: 1rem;
|
||||
line-height: 2rem;
|
||||
position: relative;
|
||||
transition: 0.5s;
|
||||
}
|
||||
|
||||
#forkongithub a:hover {
|
||||
background: #c11;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
#forkongithub a::before, #forkongithub a::after {
|
||||
content: "";
|
||||
width: 100%;
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 1px;
|
||||
left: 0;
|
||||
height: 1px;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
#forkongithub a::after {
|
||||
bottom: 1px;
|
||||
top: auto;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 0) {
|
||||
#forkongithub {
|
||||
position: fixed;
|
||||
display: block;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 200px;
|
||||
overflow: hidden;
|
||||
height: 200px;
|
||||
z-index: 9999;
|
||||
}
|
||||
|
||||
#forkongithub a {
|
||||
width: 200px;
|
||||
box-sizing: content-box;
|
||||
position: absolute;
|
||||
top: 60px;
|
||||
left: -60px;
|
||||
transform: rotate(-45deg);
|
||||
-webkit-transform: rotate(-45deg);
|
||||
-ms-transform: rotate(-45deg);
|
||||
-moz-transform: rotate(-45deg);
|
||||
-o-transform: rotate(-45deg);
|
||||
box-shadow: 1px 1px 3px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
}</style>
|
||||
<span style="box-sizing: content-box;" id="forkongithub"><a style="box-sizing: content-box;"
|
||||
href="https://github.com/bluejamesbond/Scribe.js"
|
||||
target="_blank">Fork me on GitHub</a></span>
|
||||
<div class="header">
|
||||
<div class="header__logo"></div>
|
||||
</div>
|
||||
<div class="notification">
|
||||
<div class="wrapper">
|
||||
<div class="text">Completed</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="content">
|
||||
<div style="width:100%;height:100%;">
|
||||
<div class="main">
|
||||
<div class="important">
|
||||
<h1>Scribe.js</h1>
|
||||
<h4>Simple. Clean. Minimal NodeJS logging framework<br/>that is accessible from anywhere in the world.
|
||||
</h4>
|
||||
</div>
|
||||
<div class="normal">
|
||||
<h2>Demo</h2>
|
||||
<h5>Enter some text to be printed out. JSON is supported as well. The text will be printed out by the
|
||||
server using a scribe-enabled console. You can then view the logs to see your content
|
||||
displayed.</h5>
|
||||
<div style="width:100%;margin-top:20px;">
|
||||
<input id="input-tag" style="margin-right:10px;color:#21ce99;" placeholder="Enter tag"/>
|
||||
<input id="input-msg" style="margin-right:10px;width:565px" placeholder="Enter message"/>
|
||||
<button id="submit" class="button" href="#">Console.log</button>
|
||||
</div>
|
||||
<a class="direct" target="_frame" href="scribe">
|
||||
<div>▶</div>
|
||||
<div>Access System Logs</div>
|
||||
</a>
|
||||
</div>
|
||||
<div class="normal">
|
||||
<h2>Web Access</h2>
|
||||
<h5>All your logs can be accessed from the web with a rich HTML interface.</h5>
|
||||
<img alt="WebPanel screenshot" src="img/screenshot-1.png"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
4
herokuapp/public/js/jquery-2.1.3.min.js
vendored
4
herokuapp/public/js/jquery-2.1.3.min.js
vendored
File diff suppressed because one or more lines are too long
|
@ -1,54 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<base target="_frame" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>ScribeJS Demo</title>
|
||||
<link href='https://fonts.googleapis.com/css?family=Open+Sans:400,300,600,700' rel='stylesheet' type='text/css'>
|
||||
<link href='https://fonts.googleapis.com/css?family=Ubuntu+Mono:400,700' rel='stylesheet' type='text/css'>
|
||||
<link href="css/style.css" rel="stylesheet" type="text/css">
|
||||
<script src ="js/jquery-2.1.3.min.js" type="text/javascript"></script>
|
||||
<script src ="js/index.js" type="text/javascript"></script>
|
||||
</head>
|
||||
<body>
|
||||
<style>#forkongithub a{background:#000;color:#fff;text-decoration:none;font-family:arial,sans-serif;text-align:center;box-sizing: content-box;font-weight:bold;padding:5px 40px;font-size:1rem;line-height:2rem;position:relative;transition:0.5s;}#forkongithub a:hover{background:#c11;color:#fff;}#forkongithub a::before,#forkongithub a::after{content:"";width:100%;display:block;position:absolute;top:1px;left:0;height:1px;background:#fff;}#forkongithub a::after{bottom:1px;top:auto;}@media screen and (min-width:0){#forkongithub{position:fixed;display:block;top:0;left:0;width:200px;overflow:hidden;height:200px;z-index:9999;}#forkongithub a{width:200px;box-sizing: content-box;position:absolute;top:60px;left:-60px;transform:rotate(-45deg);-webkit-transform:rotate(-45deg);-ms-transform:rotate(-45deg);-moz-transform:rotate(-45deg);-o-transform:rotate(-45deg);box-shadow:1px 1px 3px rgba(0,0,0,0.2);}}</style><span style="box-sizing: content-box;" id="forkongithub"><a style="box-sizing: content-box;" href="https://github.com/bluejamesbond/Scribe.js" target="_blank">Fork me on GitHub</a></span>
|
||||
<div class = "header">
|
||||
<div class = "header__logo"></div>
|
||||
</div>
|
||||
<div class = "notification">
|
||||
<div class = "wrapper">
|
||||
<div class = "text">Completed</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class = "content">
|
||||
<div style="width:100%;height:100%;">
|
||||
<div class = "main">
|
||||
<div class = "important">
|
||||
<h1>Scribe.js</h1>
|
||||
<h4>Simple. Clean. Minimal NodeJS logging framework<br/>that is accessible from anywhere in the world.</h4>
|
||||
</div>
|
||||
<div class = "normal">
|
||||
<h2>Demo</h2>
|
||||
<h5>Enter some text to be printed out. JSON is supported as well. The text will be printed out by the server using a scribe-enabled console. You can then view the logs to see your content displayed.</h5>
|
||||
<div style="width:100%;margin-top:20px;">
|
||||
<input id="input-tag" style="margin-right:10px;color:#21ce99;" placeholder="Enter tag"/>
|
||||
<input id="input-msg" style="margin-right:10px;width:565px" placeholder="Enter message"/>
|
||||
<button id="submit" class="button" href="#">Console.log</button>
|
||||
</div>
|
||||
<a class="direct" target="_frame" href="logs">
|
||||
<div>▶</div>
|
||||
<div>Access System Logs</div>
|
||||
</a>
|
||||
</div>
|
||||
<div class = "normal">
|
||||
<h2>Web Access</h2>
|
||||
<h5>All your logs can be accessed from the web with a rich HTML interface.</h5>
|
||||
<img alt="WebPanel screenshot" src = "img/screenshot-1.png"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
2
index.js
2
index.js
|
@ -1 +1 @@
|
|||
module.exports = require('./dist/scribe').default;
|
||||
module.exports = require('./dist/scribe');
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"main": "http://localhost:4005/scribe/",
|
||||
"main": "",
|
||||
"name": "Scribe",
|
||||
"description": "Scribe logging",
|
||||
"version": "0.1.0",
|
||||
|
|
28
package.json
28
package.json
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "scribe-js",
|
||||
"description": "Comprehensive logging framework with online interface",
|
||||
"version": "3.0.0-alpha.1",
|
||||
"version": "3.0.0-alpha.5",
|
||||
"homepage": "https://github.com/bluejamesbond/Scribe.js",
|
||||
"keywords": [],
|
||||
"scripts": {
|
||||
|
@ -30,10 +30,23 @@
|
|||
"main": "index.js",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"JSONStream": "1.1.1",
|
||||
"async": "1.5.2",
|
||||
"babel-plugin-syntax-async-functions": "6.5.0",
|
||||
"babel-plugin-syntax-class-properties": "6.5.0",
|
||||
"babel-plugin-transform-async-functions": "6.5.0",
|
||||
"babel-plugin-transform-async-to-module-method": "6.7.0",
|
||||
"babel-plugin-transform-class-properties": "6.5.0",
|
||||
"babel-plugin-transform-export-default-name": "1.0.4",
|
||||
"babel-plugin-transform-object-rest-spread": "6.5.0",
|
||||
"babel-plugin-transform-runtime": "6.5.2",
|
||||
"babel-preset-es2015": "6.5.0",
|
||||
"babel-preset-react": "6.5.0",
|
||||
"babel-register": "6.5.2",
|
||||
"babel-runtime": "6.5.0",
|
||||
"basic-auth": "1.0.3",
|
||||
"basic-auth-connect": "1.0.0",
|
||||
"bluebird": "3.3.4",
|
||||
"body-parser": "1.15.0",
|
||||
"brace": "0.7.0",
|
||||
"callsite": "1.0.0",
|
||||
|
@ -52,9 +65,9 @@
|
|||
"rc": "1.1.6",
|
||||
"react": "0.14.7",
|
||||
"react-ace": "3.1.0",
|
||||
"react-datepicker": "0.18.0",
|
||||
"react-datepicker": "0.25.0",
|
||||
"react-dom": "0.14.7",
|
||||
"react-influx": "1.0.8",
|
||||
"react-influx": "1.1.5",
|
||||
"react-list": "0.7.14",
|
||||
"react-object-inspector": "0.1.6",
|
||||
"react-prefixr": "0.1.0",
|
||||
|
@ -62,11 +75,7 @@
|
|||
"socket.io": "1.4.3",
|
||||
"strip-ansi": "3.0.0",
|
||||
"superagent": "1.7.2",
|
||||
"underscore": "1.8.3",
|
||||
"babel-plugin-transform-object-rest-spread": "6.5.0",
|
||||
"babel-plugin-transform-runtime": "6.5.2",
|
||||
"babel-preset-es2015": "6.5.0",
|
||||
"babel-preset-react": "6.5.0"
|
||||
"underscore": "1.8.3"
|
||||
},
|
||||
"readmeFilename": "README.md",
|
||||
"devDependencies": {
|
||||
|
@ -87,6 +96,7 @@
|
|||
"license-report": "2.0.0",
|
||||
"load-grunt-tasks": "3.4.0",
|
||||
"mocha": "2.4.5",
|
||||
"time-grunt": "1.3.0"
|
||||
"time-grunt": "1.3.0",
|
||||
"trace-error": "0.0.7"
|
||||
}
|
||||
}
|
||||
|
|
21
public/scripts/TweenMax.min.js
vendored
21
public/scripts/TweenMax.min.js
vendored
File diff suppressed because one or more lines are too long
22724
public/scripts/login/Bootstrap.min.js
vendored
22724
public/scripts/login/Bootstrap.min.js
vendored
File diff suppressed because one or more lines are too long
6
public/scripts/socket.min.js
vendored
6
public/scripts/socket.min.js
vendored
File diff suppressed because one or more lines are too long
848
public/scripts/three.min.js
vendored
848
public/scripts/three.min.js
vendored
File diff suppressed because one or more lines are too long
80625
public/scripts/viewer/Bootstrap.min.js
vendored
80625
public/scripts/viewer/Bootstrap.min.js
vendored
File diff suppressed because one or more lines are too long
2
public/styles/login.min.css
vendored
2
public/styles/login.min.css
vendored
File diff suppressed because one or more lines are too long
7
public/styles/login.min.css.map
Normal file
7
public/styles/login.min.css.map
Normal file
File diff suppressed because one or more lines are too long
2
public/styles/viewer.min.css
vendored
2
public/styles/viewer.min.css
vendored
File diff suppressed because one or more lines are too long
7
public/styles/viewer.min.css.map
Normal file
7
public/styles/viewer.min.css.map
Normal file
File diff suppressed because one or more lines are too long
|
@ -1,5 +1,4 @@
|
|||
* {
|
||||
font-family: 'Proxima Nova', "Helvetica", "Helvetic Neue", "Open Sans", sans-serif;
|
||||
vertical-align: top;
|
||||
box-sizing: border-box;
|
||||
outline: none;
|
||||
|
@ -30,8 +29,10 @@ code {
|
|||
"dmenu" ,
|
||||
"pop" ,
|
||||
"settings" ,
|
||||
"calendar" ,
|
||||
"archive" ,
|
||||
"console" ,
|
||||
"arrow" ,
|
||||
"menu" ;
|
||||
|
||||
@each $i in $icons {
|
||||
|
|
136
resources/styles/shared/checkbox.scss
Normal file
136
resources/styles/shared/checkbox.scss
Normal file
|
@ -0,0 +1,136 @@
|
|||
@charset "UTF-8";
|
||||
|
||||
.checkbox {
|
||||
width: 13px;
|
||||
height: 13px;
|
||||
min-height: 13px;
|
||||
min-width: 13px;
|
||||
display: inline-block;
|
||||
border: 1px solid rgba(255, 255, 255, 0.35);
|
||||
cursor: pointer;
|
||||
|
||||
.light & {
|
||||
color: #444;
|
||||
}
|
||||
|
||||
.dark & {
|
||||
color: rgb(63, 200, 41);
|
||||
}
|
||||
|
||||
.light & {
|
||||
background: rgba(0, 0, 0, 0.05);
|
||||
border: 1px solid rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.dark & {
|
||||
background: transparent;
|
||||
border: 1px solid rgba(255, 255, 255, 0.25);
|
||||
}
|
||||
|
||||
.check {
|
||||
visibility: hidden;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
border-radius: 3px;
|
||||
font-family: 'Fontello', sans-serif;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
&.checked {
|
||||
|
||||
.check {
|
||||
visibility: visible;
|
||||
|
||||
&:before {
|
||||
font-size: 12px;
|
||||
content: '';
|
||||
width: 10px;
|
||||
height: 5px;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 2px;
|
||||
border: 3px solid #555;
|
||||
border-top: none;
|
||||
border-right: none;
|
||||
background: transparent;
|
||||
transform: rotate(-45deg);
|
||||
|
||||
.dark & {
|
||||
border-color: #00FF2A;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$toggleSize: 20px;
|
||||
|
||||
&.toggle {
|
||||
width: auto;
|
||||
height: $toggleSize;
|
||||
border-radius: $toggleSize;
|
||||
background: #555;
|
||||
position: relative;
|
||||
padding-left: $toggleSize;
|
||||
border-color: rgba(255, 255, 255, 0.1);
|
||||
transition: padding 400ms, background 400ms;
|
||||
|
||||
.light & {
|
||||
background: #A3AAAF;
|
||||
border-color: rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
> .label {
|
||||
font-size: 9px;
|
||||
display: block;
|
||||
padding: 0 10px;
|
||||
text-transform: uppercase;
|
||||
font-weight: bold;
|
||||
color: #bbb;
|
||||
line-height: $toggleSize - 1px;
|
||||
letter-spacing: 1px;
|
||||
transition: color 400ms;
|
||||
.light & {
|
||||
color: #eee;
|
||||
}
|
||||
}
|
||||
|
||||
.check {
|
||||
visibility: visible;
|
||||
background: #DDD;
|
||||
border-radius: $toggleSize;
|
||||
width: $toggleSize + 4px;
|
||||
height: $toggleSize + 4px;
|
||||
position: absolute;
|
||||
top: -3px;
|
||||
left: 0;
|
||||
margin-left: -2px;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.5), 0 0 0 1px rgba(255, 255, 255, 0.15);
|
||||
transition: left 400ms, margin-left 400ms;
|
||||
|
||||
.light & {
|
||||
background: #fff;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
}
|
||||
|
||||
&.checked {
|
||||
padding-left: 0;
|
||||
padding-right: $toggleSize;
|
||||
background: #6EC53A;
|
||||
|
||||
.label {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.check {
|
||||
left: 100%;
|
||||
margin-left: -$toggleSize - 2px;
|
||||
|
||||
&:before {
|
||||
content: none;
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,12 +1,12 @@
|
|||
$color: #de930f;
|
||||
$secondaryColor: #ae720c;
|
||||
|
||||
.tether-element-attached-top .datepicker__triangle, .tether-element-attached-bottom .datepicker__triangle, .datepicker__year-read-view--down-arrow {
|
||||
.react-datepicker__tether-element-attached-top .react-datepicker__triangle, .react-datepicker__tether-element-attached-bottom .react-datepicker__triangle, .react-datepicker__year-read-view--down-arrow {
|
||||
margin-left: -8px;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.tether-element-attached-top .datepicker__triangle, .tether-element-attached-bottom .datepicker__triangle, .datepicker__year-read-view--down-arrow, .tether-element-attached-top .datepicker__triangle::before, .tether-element-attached-bottom .datepicker__triangle::before, .datepicker__year-read-view--down-arrow::before {
|
||||
.react-datepicker__tether-element-attached-top .react-datepicker__triangle, .react-datepicker__tether-element-attached-bottom .react-datepicker__triangle, .react-datepicker__year-read-view--down-arrow, .react-datepicker__tether-element-attached-top .react-datepicker__triangle::before, .react-datepicker__tether-element-attached-bottom .react-datepicker__triangle::before, .react-datepicker__year-read-view--down-arrow::before {
|
||||
box-sizing: content-box;
|
||||
position: absolute;
|
||||
border: 8px solid transparent;
|
||||
|
@ -14,7 +14,7 @@ $secondaryColor: #ae720c;
|
|||
width: 1px;
|
||||
}
|
||||
|
||||
.tether-element-attached-top .datepicker__triangle::before, .tether-element-attached-bottom .datepicker__triangle::before, .datepicker__year-read-view--down-arrow::before {
|
||||
.react-datepicker__tether-element-attached-top .react-datepicker__triangle::before, .react-datepicker__tether-element-attached-bottom .react-datepicker__triangle::before, .react-datepicker__year-read-view--down-arrow::before {
|
||||
content: "";
|
||||
z-index: -1;
|
||||
border-width: 8px;
|
||||
|
@ -22,37 +22,37 @@ $secondaryColor: #ae720c;
|
|||
border-bottom-color: transparent;
|
||||
}
|
||||
|
||||
.tether-element-attached-top .datepicker__triangle {
|
||||
.react-datepicker__tether-element-attached-top .react-datepicker__triangle {
|
||||
top: 0;
|
||||
margin-top: -8px;
|
||||
}
|
||||
|
||||
.tether-element-attached-top .datepicker__triangle, .tether-element-attached-top .datepicker__triangle::before {
|
||||
.react-datepicker__tether-element-attached-top .react-datepicker__triangle, .react-datepicker__tether-element-attached-top .react-datepicker__triangle::before {
|
||||
border-top: none;
|
||||
border-bottom-color: $secondaryColor;
|
||||
}
|
||||
|
||||
.tether-element-attached-top .datepicker__triangle::before {
|
||||
.react-datepicker__tether-element-attached-top .react-datepicker__triangle::before {
|
||||
top: -1px;
|
||||
border-bottom-color: transparent;
|
||||
}
|
||||
|
||||
.tether-element-attached-bottom .datepicker__triangle, .datepicker__year-read-view--down-arrow {
|
||||
.react-datepicker__tether-element-attached-bottom .react-datepicker__triangle, .react-datepicker__year-read-view--down-arrow {
|
||||
bottom: 0;
|
||||
margin-bottom: -8px;
|
||||
}
|
||||
|
||||
.tether-element-attached-bottom .datepicker__triangle, .datepicker__year-read-view--down-arrow, .tether-element-attached-bottom .datepicker__triangle::before, .datepicker__year-read-view--down-arrow::before {
|
||||
.react-datepicker__tether-element-attached-bottom .react-datepicker__triangle, .react-datepicker__year-read-view--down-arrow, .react-datepicker__tether-element-attached-bottom .react-datepicker__triangle::before, .react-datepicker__year-read-view--down-arrow::before {
|
||||
border-bottom: none;
|
||||
border-top-color: #fff;
|
||||
}
|
||||
|
||||
.tether-element-attached-bottom .datepicker__triangle::before, .datepicker__year-read-view--down-arrow::before {
|
||||
.react-datepicker__tether-element-attached-bottom .react-datepicker__triangle::before, .react-datepicker__year-read-view--down-arrow::before {
|
||||
bottom: -1px;
|
||||
border-top-color: transparent;
|
||||
}
|
||||
|
||||
.datepicker {
|
||||
.react-datepicker {
|
||||
font-size: 11px;
|
||||
background-color: $color;
|
||||
color: #000;
|
||||
|
@ -62,29 +62,29 @@ $secondaryColor: #ae720c;
|
|||
box-shadow: 0 0 40px rgba(0, 0, 0, 0.37);
|
||||
}
|
||||
|
||||
.datepicker__container {
|
||||
.react-datepicker__container {
|
||||
position: absolute;
|
||||
display: inline-block;
|
||||
z-index: 2147483647;
|
||||
}
|
||||
|
||||
.datepicker__triangle {
|
||||
.react-datepicker__triangle {
|
||||
position: absolute;
|
||||
left: 50px;
|
||||
}
|
||||
|
||||
.tether-target-attached-top.datepicker__container {
|
||||
.react-datepicker__tether-target-attached-top.react-datepicker__container {
|
||||
margin-top: -20px;
|
||||
}
|
||||
|
||||
.datepicker__header {
|
||||
.react-datepicker__header {
|
||||
text-align: center;
|
||||
background-color: $secondaryColor;
|
||||
padding-top: 10px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.datepicker__current-month {
|
||||
.react-datepicker__current-month {
|
||||
margin-top: 0;
|
||||
color: #FFF;
|
||||
font-weight: bold;
|
||||
|
@ -96,11 +96,11 @@ $secondaryColor: #ae720c;
|
|||
display: inline-block;
|
||||
}
|
||||
|
||||
.datepicker__current-month--hasYearDropdown {
|
||||
.react-datepicker__current-month--hasYearDropdown {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.datepicker__navigation {
|
||||
.react-datepicker__navigation {
|
||||
line-height: 24px;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
|
@ -110,25 +110,25 @@ $secondaryColor: #ae720c;
|
|||
border: 6px solid transparent;
|
||||
}
|
||||
|
||||
.datepicker__navigation--previous {
|
||||
.react-datepicker__navigation--previous {
|
||||
left: 10px;
|
||||
border-right-color: #fff;
|
||||
}
|
||||
|
||||
.datepicker__navigation--previous:hover {
|
||||
.react-datepicker__navigation--previous:hover {
|
||||
border-right-color: #b3b3b3;
|
||||
}
|
||||
|
||||
.datepicker__navigation--next {
|
||||
.react-datepicker__navigation--next {
|
||||
right: 10px;
|
||||
border-left-color: #fff;
|
||||
}
|
||||
|
||||
.datepicker__navigation--next:hover {
|
||||
.react-datepicker__navigation--next:hover {
|
||||
border-left-color: #b3b3b3;
|
||||
}
|
||||
|
||||
.datepicker__navigation--years {
|
||||
.react-datepicker__navigation--years {
|
||||
position: relative;
|
||||
top: 0;
|
||||
display: block;
|
||||
|
@ -136,39 +136,39 @@ $secondaryColor: #ae720c;
|
|||
margin-right: auto;
|
||||
}
|
||||
|
||||
.datepicker__navigation--years-previous {
|
||||
.react-datepicker__navigation--years-previous {
|
||||
top: 4px;
|
||||
border-top-color: #ccc;
|
||||
}
|
||||
|
||||
.datepicker__navigation--years-previous:hover {
|
||||
.react-datepicker__navigation--years-previous:hover {
|
||||
border-top-color: #b3b3b3;
|
||||
}
|
||||
|
||||
.datepicker__navigation--years-upcoming {
|
||||
.react-datepicker__navigation--years-upcoming {
|
||||
top: -4px;
|
||||
border-bottom-color: #ccc;
|
||||
}
|
||||
|
||||
.datepicker__navigation--years-upcoming:hover {
|
||||
.react-datepicker__navigation--years-upcoming:hover {
|
||||
border-bottom-color: #b3b3b3;
|
||||
}
|
||||
|
||||
.datepicker__week-day {
|
||||
.react-datepicker__week-day {
|
||||
color: #ccc;
|
||||
display: inline-block;
|
||||
width: 28px;
|
||||
line-height: 24px;
|
||||
}
|
||||
|
||||
.datepicker__month {
|
||||
.react-datepicker__month {
|
||||
margin: 5px;
|
||||
text-align: center;
|
||||
background: $color;
|
||||
}
|
||||
|
||||
.datepicker__day {
|
||||
color: rgba(255, 255, 255, 0.44);
|
||||
.react-datepicker__day {
|
||||
color: rgba(255, 255, 255, 0.6);
|
||||
display: inline-block;
|
||||
width: 24px;
|
||||
line-height: 21px;
|
||||
|
@ -182,44 +182,45 @@ $secondaryColor: #ae720c;
|
|||
border-radius: 24px;
|
||||
}
|
||||
|
||||
.datepicker__day:hover {
|
||||
.react-datepicker__day:hover {
|
||||
/* border-radius: 4px; */
|
||||
background-color: #f0f0f0;
|
||||
/* color: $color; */
|
||||
color: $color;
|
||||
}
|
||||
|
||||
.datepicker__day--today {
|
||||
.react-datepicker__day--today {
|
||||
/* font-weight: bold; */
|
||||
}
|
||||
|
||||
.datepicker__day--selected, .datepicker__day--in-range {
|
||||
.react-datepicker__day--selected, .react-datepicker__day--in-range {
|
||||
/* border-radius: 4px; */
|
||||
background-color: rgba(255, 255, 255, 0.43);
|
||||
color: #fff;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.datepicker__day--selected:hover, .datepicker__day--in-range:hover {
|
||||
.react-datepicker__day--selected:hover, .react-datepicker__day--in-range:hover {
|
||||
/* background-color: #1d5d90; */
|
||||
/* color: red; */
|
||||
color: $color;
|
||||
}
|
||||
|
||||
.datepicker__day--disabled {
|
||||
.react-datepicker__day--disabled {
|
||||
cursor: default;
|
||||
color: #ccc;
|
||||
color: rgba(255, 255, 255, 0.15) !important;
|
||||
}
|
||||
|
||||
.datepicker__day--disabled:hover {
|
||||
.react-datepicker__day--disabled:hover {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.datepicker__input-container {
|
||||
.react-datepicker__input-container {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.datepicker__input {
|
||||
.react-datepicker__input {
|
||||
position: relative;
|
||||
font-size: 13px;
|
||||
border-radius: 4px;
|
||||
|
@ -229,17 +230,17 @@ $secondaryColor: #ae720c;
|
|||
padding: 6px 10px 5px;
|
||||
}
|
||||
|
||||
.datepicker__input:focus {
|
||||
.react-datepicker__input:focus {
|
||||
outline: none;
|
||||
border-color: #aeaeae;
|
||||
box-shadow: inset 0 2px 2px #e9e9e9, 0 0 10px 0 rgba(73, 107, 125, 0.3);
|
||||
}
|
||||
|
||||
.datepicker__input:not(:valid) ~ .close-icon {
|
||||
.react-datepicker__input:not(:valid) ~ .close-icon {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.datepicker__year-read-view {
|
||||
.react-datepicker__year-read-view {
|
||||
width: 50%;
|
||||
left: 25%;
|
||||
position: absolute;
|
||||
|
@ -248,15 +249,15 @@ $secondaryColor: #ae720c;
|
|||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.datepicker__year-read-view:hover {
|
||||
.react-datepicker__year-read-view:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.datepicker__year-read-view:hover .datepicker__year-read-view--down-arrow {
|
||||
.react-datepicker__year-read-view:hover .react-datepicker__year-read-view--down-arrow {
|
||||
border-top-color: #b3b3b3;
|
||||
}
|
||||
|
||||
.datepicker__year-read-view--down-arrow {
|
||||
.react-datepicker__year-read-view--down-arrow {
|
||||
border-top-color: #ccc;
|
||||
margin-bottom: 3px;
|
||||
left: 5px;
|
||||
|
@ -265,12 +266,12 @@ $secondaryColor: #ae720c;
|
|||
border-width: 6px;
|
||||
}
|
||||
|
||||
.datepicker__year-read-view--selected-year {
|
||||
.react-datepicker__year-read-view--selected-year {
|
||||
right: 6px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.datepicker__year-dropdown {
|
||||
.react-datepicker__year-dropdown {
|
||||
background-color: #f0f0f0;
|
||||
position: absolute;
|
||||
width: 50%;
|
||||
|
@ -281,11 +282,11 @@ $secondaryColor: #ae720c;
|
|||
border: 1px solid #aeaeae;
|
||||
}
|
||||
|
||||
.datepicker__year-dropdown:hover {
|
||||
.react-datepicker__year-dropdown:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.datepicker__year-option {
|
||||
.react-datepicker__year-option {
|
||||
line-height: 20px;
|
||||
width: 100%;
|
||||
display: block;
|
||||
|
@ -293,12 +294,12 @@ $secondaryColor: #ae720c;
|
|||
margin-right: auto;
|
||||
}
|
||||
|
||||
.datepicker__year-option:first-of-type {
|
||||
.react-datepicker__year-option:first-of-type {
|
||||
border-top-left-radius: 4px;
|
||||
border-top-right-radius: 4px;
|
||||
}
|
||||
|
||||
.datepicker__year-option:last-of-type {
|
||||
.react-datepicker__year-option:last-of-type {
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
|
@ -307,25 +308,30 @@ $secondaryColor: #ae720c;
|
|||
border-bottom-right-radius: 4px;
|
||||
}
|
||||
|
||||
.datepicker__year-option:hover {
|
||||
.react-datepicker__year-option:hover {
|
||||
background-color: #ccc;
|
||||
}
|
||||
|
||||
.datepicker__year-option:hover .datepicker__navigation--years-upcoming {
|
||||
.react-datepicker__year-option:hover .react-datepicker__navigation--years-upcoming {
|
||||
border-bottom-color: #b3b3b3;
|
||||
}
|
||||
|
||||
.datepicker__year-option:hover .datepicker__navigation--years-previous {
|
||||
.react-datepicker__year-option:hover .react-datepicker__navigation--years-previous {
|
||||
border-top-color: #b3b3b3;
|
||||
}
|
||||
|
||||
.datepicker__year-option--selected {
|
||||
.react-datepicker__year-option--selected {
|
||||
position: absolute;
|
||||
left: 30px;
|
||||
}
|
||||
|
||||
.datepicker__header .datepicker__day {
|
||||
.react-datepicker__header .react-datepicker__day {
|
||||
background: transparent !important;
|
||||
color: #FFF !important;
|
||||
cursor: default;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.react-datepicker__tether-element {
|
||||
z-index: 9999;
|
||||
}
|
File diff suppressed because one or more lines are too long
|
@ -4,19 +4,37 @@
|
|||
@import "shared/scroll";
|
||||
@import "shared/layouts";
|
||||
@import "shared/datepicker";
|
||||
@import "shared/checkbox";
|
||||
|
||||
html, body {
|
||||
background: #222;
|
||||
font-size: 0.95rem;
|
||||
line-height: 1.5rem;
|
||||
font-family: 'Proxima Nova', "Helvetica", "Helvetic Neue", "Open Sans", sans-serif;
|
||||
&.blue {
|
||||
background: #242831;
|
||||
}
|
||||
|
||||
transition: background 1500ms;
|
||||
}
|
||||
|
||||
.animate-height {
|
||||
transition: max-height 600ms, min-height 600ms, height 600ms;
|
||||
}
|
||||
|
||||
.visibility-hidden {
|
||||
visibility: hidden;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.animate-visibility {
|
||||
transition: visibility 600ms, opacity 600ms;
|
||||
}
|
||||
|
||||
.header {
|
||||
width: 100%;
|
||||
height: 50px;
|
||||
min-height: 50px;
|
||||
background: #222;
|
||||
box-shadow: 0 1px 10px #111;
|
||||
height: 125px;
|
||||
min-height: 125px;
|
||||
position: relative;
|
||||
z-index: 3;
|
||||
}
|
||||
|
@ -24,12 +42,11 @@ html, body {
|
|||
.group {
|
||||
.title {
|
||||
text-transform: uppercase;
|
||||
color: #444;
|
||||
color: #E4E4E4;
|
||||
font-weight: bold;
|
||||
height: 50px;
|
||||
line-height: 50px;
|
||||
letter-spacing: 1px;
|
||||
border-bottom: 1px solid #ddd;
|
||||
padding-left: 20px;
|
||||
font-size: 80%;
|
||||
}
|
||||
|
@ -51,7 +68,7 @@ html, body {
|
|||
.sidebar {
|
||||
width: 200px;
|
||||
min-width: 170px;
|
||||
background: #fff;
|
||||
background: rgba(0, 0, 0, .5);
|
||||
}
|
||||
|
||||
.bgRed {
|
||||
|
@ -91,7 +108,7 @@ html, body {
|
|||
text-transform: uppercase;
|
||||
color: #CF358C;
|
||||
&.persistent {
|
||||
color: #32BBC7;
|
||||
color: rgb(155, 187, 220);
|
||||
}
|
||||
&.stroke {
|
||||
font-size: 9px;
|
||||
|
@ -107,25 +124,24 @@ html, body {
|
|||
border-radius: 2px;
|
||||
letter-spacing: 1px;
|
||||
font-size: 8px;
|
||||
&.new-entry {
|
||||
background: #3F51B5;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.pre {
|
||||
margin-right: 3px;
|
||||
@extend .monospaced;
|
||||
font-size: 85%;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.yellow {
|
||||
color: #D4D400;
|
||||
color: rgb(93, 176, 215);
|
||||
}
|
||||
|
||||
.entry {
|
||||
font-size: 80%;
|
||||
line-height: 130%;
|
||||
line-height: 110%;
|
||||
padding: 3px 0;
|
||||
white-space: pre;
|
||||
font-family: Meslo, Menlo, Monaco, Consolas, monospace;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.line {
|
||||
|
@ -133,7 +149,6 @@ html, body {
|
|||
text-align: right;
|
||||
display: inline-block;
|
||||
margin-right: 5px;
|
||||
@extend .monospaced;
|
||||
font-size: 80%;
|
||||
}
|
||||
|
||||
|
@ -172,7 +187,7 @@ html, body {
|
|||
.ObjectInspector {
|
||||
cursor: default;
|
||||
display: block;
|
||||
color: #FFF;
|
||||
color: rgb(189, 198, 207);
|
||||
}
|
||||
|
||||
.ObjectInspector-unselectable {
|
||||
|
@ -204,7 +219,7 @@ html, body {
|
|||
}
|
||||
|
||||
.ObjectInspector-object-name {
|
||||
color: #26F714;
|
||||
color: rgb(227, 110, 236);
|
||||
}
|
||||
|
||||
.ObjectInspector-object-value-null, .ObjectInspector-object-value-undefined {
|
||||
|
@ -212,7 +227,7 @@ html, body {
|
|||
}
|
||||
|
||||
.ObjectInspector-object-value-string {
|
||||
color: #525252;
|
||||
color: rgb(242, 151, 102);
|
||||
}
|
||||
|
||||
.ObjectInspector-object-value-symbol {
|
||||
|
@ -306,7 +321,7 @@ html, body {
|
|||
font-size: 10px;
|
||||
width: 60px;
|
||||
max-width: 60px;
|
||||
background: #444;
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
color: #999;
|
||||
letter-spacing: 1px;
|
||||
border-radius: $fieldRadius 0 0 $fieldRadius;
|
||||
|
@ -316,19 +331,30 @@ html, body {
|
|||
text-transform: uppercase;
|
||||
}
|
||||
.input {
|
||||
background: #272822;
|
||||
background: transparent;
|
||||
border-radius: 0 $fieldRadius $fieldRadius 0;
|
||||
padding: 5px 5px 4px;
|
||||
border: 1px solid #444;
|
||||
padding: 6px 5px 4px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
* {
|
||||
font-size: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
&.readonly {
|
||||
.input {
|
||||
border: transparent;
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ace-monokai {
|
||||
background: transparent !important;
|
||||
}
|
||||
|
||||
.button {
|
||||
background: #2589DE;
|
||||
border-radius: 20px;
|
||||
border-radius: 2px;
|
||||
color: #FFF;
|
||||
font-size: 70%;
|
||||
text-transform: uppercase;
|
||||
|
@ -339,6 +365,8 @@ html, body {
|
|||
transition: background 50ms;
|
||||
height: 26px;
|
||||
line-height: 26px;
|
||||
display: block;
|
||||
text-decoration: none;
|
||||
&.disabled {
|
||||
cursor: none;
|
||||
pointer-events: none;
|
||||
|
@ -360,4 +388,204 @@ html, body {
|
|||
|
||||
.bgPurple {
|
||||
background: #6C07B9 !important;
|
||||
}
|
||||
|
||||
.big-input {
|
||||
letter-spacing: 1px;
|
||||
font-size: 15px;
|
||||
width: 100%;
|
||||
border: none;
|
||||
color: #FFF;
|
||||
outline: none;
|
||||
margin: 0;
|
||||
font-weight: 100;
|
||||
padding: 17px 17px 5px;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.ggroup {
|
||||
padding: 0 2px;
|
||||
border-left: 1px solid rgba(148, 148, 148, 0.07);
|
||||
transition: background 1200ms;
|
||||
&:first-child {
|
||||
border-left: none;
|
||||
}
|
||||
.gg {
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
|
||||
.bar {
|
||||
background: #333;
|
||||
position: relative;
|
||||
max-width: 3px;
|
||||
margin-right: 2px;
|
||||
min-width: 1px;
|
||||
transition: height 300ms, top 300ms, background 300ms;
|
||||
&:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
&.log {
|
||||
background: green;
|
||||
}
|
||||
&.error {
|
||||
background: red;
|
||||
}
|
||||
&.info {
|
||||
background: cyan;
|
||||
}
|
||||
&.debug {
|
||||
background: yellow;
|
||||
}
|
||||
&.trace {
|
||||
background: magenta;
|
||||
}
|
||||
&.express {
|
||||
background: #423eff;
|
||||
}
|
||||
}
|
||||
|
||||
.ObjectInspector-object-preview {
|
||||
.ObjectInspector-object-value-string {
|
||||
max-height: 24px;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
|
||||
.checkbox {
|
||||
margin: 0 8px 0 0;
|
||||
border-radius: 2px;
|
||||
background: #444;
|
||||
border: none;
|
||||
.check:before {
|
||||
border-color: rgb(146, 247, 29) !important;
|
||||
}
|
||||
}
|
||||
|
||||
.red-selected {
|
||||
background: rgba(255, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.time-series-label {
|
||||
position: absolute;
|
||||
left: 3px;
|
||||
right: 3px;
|
||||
top: 5px;
|
||||
font-size: 8px;
|
||||
text-align: center;
|
||||
color: rgba(255, 255, 255, 0.1);
|
||||
letter-spacing: 1px;
|
||||
line-height: 10px;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.date-holder {
|
||||
height: 14px;
|
||||
position: relative;
|
||||
top: -1px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.react-datepicker__input-container {
|
||||
input {
|
||||
display: inline-block;
|
||||
border-radius: 2px;
|
||||
color: #FFF;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 1px;
|
||||
font-weight: normal;
|
||||
font-size: 9px !important;
|
||||
line-height: 14px;
|
||||
margin-right: 5px;
|
||||
background: rgba(255, 255, 255, 0.16);
|
||||
outline: none;
|
||||
border: none;
|
||||
padding: 0 15px 0 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.react-datepicker__input-container::after {
|
||||
font-family: 'Fontello';
|
||||
content: '';
|
||||
position: absolute;
|
||||
right: 8px;
|
||||
font-size: 80%;
|
||||
top: 0;
|
||||
pointer-events: none;
|
||||
line-height: 14px;
|
||||
color: rgba(255, 255, 255, 0.41);
|
||||
}
|
||||
|
||||
.date {
|
||||
}
|
||||
|
||||
.date-to {
|
||||
line-height: 13px;
|
||||
margin-right: 6px;
|
||||
color: rgba(255, 255, 255, 0.28);
|
||||
}
|
||||
|
||||
.tooltip {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.tooltip .tooltiptext {
|
||||
visibility: hidden;
|
||||
width: 120px;
|
||||
background-color: black;
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
padding: 5px 0;
|
||||
border-radius: 3px;
|
||||
position: absolute;
|
||||
font-size: 11px;
|
||||
font-weight: bold;
|
||||
letter-spacing: 1px;
|
||||
opacity: 0;
|
||||
transition: opacity 1s;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
text-transform: uppercase;
|
||||
z-index: 99999;
|
||||
line-height: 11px;
|
||||
border: 1px solid #222;
|
||||
}
|
||||
|
||||
.tooltip:hover .tooltiptext {
|
||||
visibility: visible;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.tooltip-top {
|
||||
bottom: 125%;
|
||||
left: 50%;
|
||||
margin-left: -60px;
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.arrow-expand {
|
||||
position: absolute;
|
||||
right: 0px;
|
||||
cursor: pointer;
|
||||
top: 0px;
|
||||
bottom: -10px;
|
||||
width: 40px;
|
||||
text-align: center;
|
||||
color: rgba(255, 255, 255, 1);
|
||||
opacity: 0.5;
|
||||
line-height: 50px;
|
||||
transition: opacity 300ms, transform 300ms;
|
||||
transform-origin: 50% 50%;
|
||||
&.rotate-down {
|
||||
transform: rotate(-180deg);
|
||||
}
|
||||
}
|
||||
|
||||
.arrow-expand:hover {
|
||||
opacity: 1;
|
||||
}
|
Binary file not shown.
Before Width: | Height: | Size: 645 KiB After Width: | Height: | Size: 228 KiB |
42
scripts/scribe-serialize.js
Normal file
42
scripts/scribe-serialize.js
Normal file
|
@ -0,0 +1,42 @@
|
|||
var MongoClient = require('mongodb').MongoClient;
|
||||
var url = 'mongodb://localhost:27017/scribe';
|
||||
|
||||
MongoClient.connect(url, function (err, db) {
|
||||
if (err) return console.error(err);
|
||||
|
||||
var Entry = db.collection('entries');
|
||||
var cursor = Entry.find({serialized: {$exists: false}});
|
||||
|
||||
cursor.on('data', function (doc) {
|
||||
if (doc.serialized) {
|
||||
return cursor.resume();
|
||||
}
|
||||
|
||||
cursor.pause();
|
||||
|
||||
Entry.update({_id: doc._id},
|
||||
{$set: {serialized: JSON.stringify(doc)}},
|
||||
err => {
|
||||
if (err) {
|
||||
console.error(err);
|
||||
cursor.close();
|
||||
return;
|
||||
}
|
||||
|
||||
cursor.resume();
|
||||
});
|
||||
});
|
||||
|
||||
cursor.on('error', function (err) {
|
||||
console.error(err);
|
||||
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
cursor.on('end', function () {
|
||||
db.close();
|
||||
|
||||
console.log('Finished. Exiting...');
|
||||
process.exit(0);
|
||||
});
|
||||
});
|
|
@ -29,8 +29,78 @@ export function parse(str, reviver) {
|
|||
});
|
||||
}
|
||||
|
||||
const stringifyOnce = function (obj, replacer, indent) {
|
||||
var printedObjects = [];
|
||||
var printedObjectKeys = [];
|
||||
|
||||
function printOnceReplacer(key, value) {
|
||||
if (printedObjects.length > 2000) { // browsers will not print more than 20K, I don't see the point to allow 2K.. algorithm will not be fast anyway if we have too many objects
|
||||
return 'object too long';
|
||||
}
|
||||
var printedObjIndex = false;
|
||||
printedObjects.forEach(function (obj, index) {
|
||||
if (obj === value) {
|
||||
printedObjIndex = index;
|
||||
}
|
||||
});
|
||||
|
||||
if (key == '') { //root element
|
||||
printedObjects.push(obj);
|
||||
printedObjectKeys.push("root");
|
||||
return value;
|
||||
}
|
||||
|
||||
else if (printedObjIndex + "" != "false" && typeof(value) == "object") {
|
||||
if (printedObjectKeys[printedObjIndex] == "root") {
|
||||
return "(pointer to root)";
|
||||
} else {
|
||||
return "(see " + ((!!value && !!value.constructor) ? value.constructor.name.toLowerCase() : typeof(value)) + " with key " + printedObjectKeys[printedObjIndex] + ")";
|
||||
}
|
||||
} else {
|
||||
|
||||
var qualifiedKey = key || "(empty key)";
|
||||
printedObjects.push(value);
|
||||
printedObjectKeys.push(qualifiedKey);
|
||||
if (replacer) {
|
||||
return replacer(key, value);
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return JSON.stringify(obj, printOnceReplacer, indent);
|
||||
};
|
||||
|
||||
// http://www.z-car.com/blog/programming/how-to-escape-mongo-keys-using-node-js-in-a-flash
|
||||
function escapeKeys(obj) {
|
||||
if (!(Boolean(obj) && typeof obj == 'object'
|
||||
&& Object.keys(obj).length > 0)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Object.keys(obj).forEach(function (key) {
|
||||
if (typeof(obj[key]) == 'object') {
|
||||
escapeKeys(obj[key]);
|
||||
} else {
|
||||
if (key.indexOf('.') !== -1) {
|
||||
var newkey = key.replace(/\./g, '_dot_');
|
||||
obj[newkey] = obj[key];
|
||||
delete obj[key];
|
||||
}
|
||||
if (key.indexOf('$') !== -1) {
|
||||
var newkey = key.replace(/\$/g, '_amp_');
|
||||
obj[newkey] = obj[key];
|
||||
delete obj[key];
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
export function stringify(obj, replacer, spaces) {
|
||||
return JSON.stringify(obj, (k, v)=> {
|
||||
const objStr = stringifyOnce(obj, (k, v)=> {
|
||||
if (typeof v !== 'undefined' && v !== null) {
|
||||
if (typeof v.then === 'function') {
|
||||
return 'Promise { <pending> }'
|
||||
|
@ -43,4 +113,10 @@ export function stringify(obj, replacer, spaces) {
|
|||
|
||||
return v;
|
||||
}, spaces);
|
||||
|
||||
const objCpy = JSON.parse(objStr);
|
||||
|
||||
escapeKeys(objCpy);
|
||||
|
||||
return JSON.stringify(objCpy);
|
||||
}
|
8
src/libs/time.js
Normal file
8
src/libs/time.js
Normal file
|
@ -0,0 +1,8 @@
|
|||
export default function time() {
|
||||
if (typeof process !== 'undefined' && typeof process.hrtime === 'function') {
|
||||
const duration = process.hrtime();
|
||||
return duration[0] * 1000 + duration[1] / 1e6;
|
||||
}
|
||||
|
||||
return Date.now();
|
||||
}
|
29
src/middleware/ExpressLogger.js
Normal file
29
src/middleware/ExpressLogger.js
Normal file
|
@ -0,0 +1,29 @@
|
|||
import onFinish from 'on-finished'
|
||||
import time from '../libs/time';
|
||||
|
||||
export default class ExpressLogger {
|
||||
constructor(scribe) {
|
||||
this.options = scribe.module('middleware/ExpressLogger').options;
|
||||
this.scribe = scribe;
|
||||
}
|
||||
|
||||
getMiddleware() {
|
||||
const {scribe, options} = this;
|
||||
|
||||
return (req, res, next) => {
|
||||
let start = time();
|
||||
onFinish(res, () => {
|
||||
const {originalUrl} = req;
|
||||
for (const k of options.ignore) {
|
||||
if (new RegExp(k).test(originalUrl)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
scribe[options.expose]({req, res, duration: (time() - start)});
|
||||
});
|
||||
|
||||
next();
|
||||
}
|
||||
}
|
||||
}
|
1
src/middleware/index.js
Normal file
1
src/middleware/index.js
Normal file
|
@ -0,0 +1 @@
|
|||
export {default as ExpressLogger} from './ExpressLogger';
|
|
@ -1,15 +1,6 @@
|
|||
import Console from './Console'
|
||||
import stack from 'callsite'
|
||||
import onFinish from 'on-finished'
|
||||
|
||||
function time() {
|
||||
if (typeof process !== 'undefined' && typeof process.hrtime === 'function') {
|
||||
const duration = process.hrtime();
|
||||
return duration[0] * 1000 + duration[1] / 1e6;
|
||||
}
|
||||
|
||||
return Date.now();
|
||||
}
|
||||
import time from '../libs/time';
|
||||
|
||||
export default class extends Console {
|
||||
constructor(app, id) {
|
||||
|
@ -37,25 +28,6 @@ export default class extends Console {
|
|||
return this;
|
||||
}
|
||||
|
||||
middleware(expose = 'express', ignore = [/(scribe)/g]) {
|
||||
this.expose(expose);
|
||||
|
||||
ignore = Array.isArray(ignore) ? ignore : [ignore];
|
||||
|
||||
return (req, res, next) => {
|
||||
let start = time();
|
||||
onFinish(res, () => {
|
||||
const {originalUrl} = req;
|
||||
for (const k of ignore) {
|
||||
if (new RegExp(k).test(originalUrl)) return;
|
||||
}
|
||||
|
||||
this[expose]({req, res, duration: (time() - start)});
|
||||
});
|
||||
next();
|
||||
}
|
||||
}
|
||||
|
||||
tag(...tags) {
|
||||
this.transient('tags', tags);
|
||||
|
||||
|
@ -74,7 +46,7 @@ export default class extends Console {
|
|||
}
|
||||
|
||||
trace(text) {
|
||||
this.transient('callsite', this.callSite(4));
|
||||
this.transient('callsite', this.callSite(2));
|
||||
return this.out('trace', new Error(text));
|
||||
}
|
||||
|
||||
|
@ -84,9 +56,9 @@ export default class extends Console {
|
|||
throw new Error(`No such label: ${label}`);
|
||||
}
|
||||
|
||||
this._times.delete(label);
|
||||
this.captureStackTrace();
|
||||
|
||||
this.transient('callsite', this.callSite(5));
|
||||
this._times.delete(label);
|
||||
|
||||
const duration = time() - start;
|
||||
|
|
@ -2,14 +2,27 @@ import stack from 'callsite'
|
|||
import async from 'async'
|
||||
|
||||
export default class {
|
||||
constructor(app = 'Scribe', id = process.pid) {
|
||||
constructor(config) {
|
||||
this._transient = {};
|
||||
this._persistent = {};
|
||||
this._pipelines = {};
|
||||
this._exposed = {};
|
||||
this._config = config;
|
||||
|
||||
this.persistent('app', app);
|
||||
this.persistent('id', id);
|
||||
this.persistent('app', this._config.app);
|
||||
this.persistent('id', this._config.id);
|
||||
}
|
||||
|
||||
module(namespace) {
|
||||
if (namespace) {
|
||||
const options = this._config.module[namespace] || {};
|
||||
|
||||
options.isSet = () => Object.keys(options).length;
|
||||
|
||||
return {options};
|
||||
}
|
||||
|
||||
return this._config;
|
||||
}
|
||||
|
||||
transient(key, value) {
|
||||
|
@ -30,18 +43,28 @@ export default class {
|
|||
return this;
|
||||
}
|
||||
|
||||
exposed() {
|
||||
exposed(expose) {
|
||||
if (expose) {
|
||||
return !!this._exposed[expose];
|
||||
}
|
||||
|
||||
return Object.keys(this._exposed);
|
||||
}
|
||||
|
||||
callSite(idx = 5) {
|
||||
captureStackTrace(idx = 3) {
|
||||
if (!this.transient('callsite')) {
|
||||
return this.transient('callsite', this.callSite(idx));
|
||||
}
|
||||
}
|
||||
|
||||
callSite(idx = 3) {
|
||||
try {
|
||||
const site = stack()[idx];
|
||||
|
||||
return {
|
||||
line: site.getLineNumber(),
|
||||
file: site.getFileName(),
|
||||
func: site.getFunctionName()
|
||||
func: site.getFunctionName() || 'anonymous'
|
||||
};
|
||||
|
||||
return site;
|
||||
|
@ -55,6 +78,8 @@ export default class {
|
|||
}
|
||||
|
||||
out(expose, ...args) {
|
||||
this.captureStackTrace();
|
||||
|
||||
return new Promise((resolve, reject)=> {
|
||||
if (!this._pipelines[expose]) {
|
||||
return resolve();
|
2
src/reader/index.js
Normal file
2
src/reader/index.js
Normal file
|
@ -0,0 +1,2 @@
|
|||
export {default as BasicConsole} from './BasicConsole';
|
||||
export {default as Console} from './Console';
|
1
src/router/index.js
Normal file
1
src/router/index.js
Normal file
|
@ -0,0 +1 @@
|
|||
export {default as Viewer} from './Viewer';
|
185
src/router/viewer.js
Normal file
185
src/router/viewer.js
Normal file
|
@ -0,0 +1,185 @@
|
|||
import express, {Router} from 'express';
|
||||
import mongoose from 'mongoose';
|
||||
import jade from 'jade';
|
||||
import basicAuth from 'basic-auth-connect';
|
||||
import bodyParser from 'body-parser'
|
||||
import JSONStream from 'JSONStream';
|
||||
import NwBuilder from 'nw-builder';
|
||||
import nativePackage from '../../native/package.json';
|
||||
import {format} from 'url';
|
||||
import {EntrySchema} from '../writer/MongoDB';
|
||||
import extend from 'extend';
|
||||
import fs from 'fs';
|
||||
|
||||
function getObject(d, def) {
|
||||
if (typeof d === 'undefined' || d === null) {
|
||||
return def || {};
|
||||
} else if (typeof d === 'object') {
|
||||
return d;
|
||||
} else {
|
||||
try {
|
||||
return JSON.parse(d);
|
||||
} catch (e) {
|
||||
return def || {};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const viewer = jade.compileFile(`${__dirname}/../../views/viewer.jade`);
|
||||
const login = jade.compileFile(`${__dirname}/../../views/login.jade`);
|
||||
|
||||
export default class Viewer {
|
||||
constructor(scribe) {
|
||||
this.scribe = scribe;
|
||||
}
|
||||
|
||||
getRouter() {
|
||||
const scribe = this.scribe;
|
||||
const routerOptions = scribe.module('router/Viewer').options;
|
||||
const clientOptions = scribe.module('router/Viewer/client').options;
|
||||
|
||||
let conn, Entry;
|
||||
|
||||
mongoose.set('debug', routerOptions.debug);
|
||||
conn = mongoose.createConnection(routerOptions.mongoUri);
|
||||
Entry = conn.model('Entry', EntrySchema);
|
||||
|
||||
const router = new Router();
|
||||
|
||||
if (routerOptions.username && routerOptions.password) {
|
||||
router.use(basicAuth(routerOptions.username, routerOptions.password));
|
||||
}
|
||||
|
||||
router.use(express.static(`${__dirname}/../../public`));
|
||||
|
||||
if (routerOptions.useBodyParser) {
|
||||
router.use(bodyParser.json());
|
||||
}
|
||||
|
||||
const render = (req, res)=> res.send(viewer({config: JSON.stringify(clientOptions)}));
|
||||
|
||||
router.get('/', render);
|
||||
router.get('/viewer', render);
|
||||
|
||||
router.post('/rest/timeseries', (req, res) => {
|
||||
try {
|
||||
if (req.body && req.body.date) {
|
||||
req.body.date = req.body.date || {};
|
||||
|
||||
if (req.body.date.$gt) req.body.date.$gt = new Date(req.body.date.$gt);
|
||||
if (req.body.date.$gte) req.body.date.$gte = new Date(req.body.date.$gte);
|
||||
if (req.body.date.$eq) req.body.date.$eq = new Date(req.body.date.$eq);
|
||||
if (req.body.date.$lt) req.body.date.$lt = new Date(req.body.date.$lt);
|
||||
if (req.body.date.$lte) req.body.date.$lte = new Date(req.body.date.$lte);
|
||||
if (req.body._id && Array.isArray(req.body._id.$in)) req.body._id.$in = req.body._id.$in.map(a => mongoose.Types.ObjectId(a));
|
||||
if (req.body._id && typeof req.body._id === 'string') req.body._id = mongoose.Types.ObjectId(req.body._id);
|
||||
}
|
||||
} catch (e) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
Entry.aggregate(
|
||||
[
|
||||
{$match: req.body},
|
||||
{
|
||||
$project: {
|
||||
hour: {
|
||||
year: {$year: '$date'},
|
||||
month: {$month: '$date'},
|
||||
day: {$dayOfMonth: '$date'},
|
||||
hour: {$hour: '$date'}
|
||||
},
|
||||
expose: '$expose'
|
||||
}
|
||||
},
|
||||
{
|
||||
$group: {
|
||||
_id: {hour: '$hour', expose: '$expose'},
|
||||
number: {$sum: 1}
|
||||
}
|
||||
}
|
||||
], (err, out) => res.json(out || []));
|
||||
});
|
||||
|
||||
router.get('/rest/db/:collection', (req, res)=> {
|
||||
if (!routerOptions.mongoUri) {
|
||||
return res.json({err: 0, docs: []});
|
||||
}
|
||||
|
||||
var selector = getObject(req.query.selector);
|
||||
var fields = typeof req.query.fields === 'string' ? req.query.fields : '';
|
||||
var sort = getObject(req.query.sort, {_id: -1});
|
||||
var limit = !isNaN(req.query.limit) ? Math.max(0, parseInt(req.query.limit)) : Number.MAX_SAFE_INTEGER;
|
||||
var skip = !isNaN(req.query.skip) ? Math.max(0, parseInt(req.query.skip)) : 0;
|
||||
|
||||
res.type('application/json');
|
||||
|
||||
const stream = Entry.find(selector)
|
||||
.select(...fields.split(' '))
|
||||
.skip(skip)
|
||||
.limit(limit)
|
||||
.sort(sort)
|
||||
.stream();
|
||||
|
||||
stream
|
||||
.pipe(JSONStream.stringify())
|
||||
.pipe(res);
|
||||
});
|
||||
|
||||
router.delete('/rest/db/:collection', (req, res)=> {
|
||||
if (!routerOptions.mongoUri) {
|
||||
res.status(410);
|
||||
return res.send();
|
||||
}
|
||||
|
||||
var ids = req.query.id;
|
||||
|
||||
try {
|
||||
ids = JSON.parse(ids);
|
||||
} catch (e) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
if (!Array.isArray(ids)) {
|
||||
ids = [req.param('id')];
|
||||
}
|
||||
|
||||
Entry.remove({_id: {$in: ids}}, err => {
|
||||
res.status(err ? 410 : 200);
|
||||
res.send();
|
||||
});
|
||||
});
|
||||
|
||||
if (routerOptions.native) {
|
||||
|
||||
}
|
||||
|
||||
return router;
|
||||
}
|
||||
|
||||
getNative() {
|
||||
const scribe = this.scribe;
|
||||
const nativeOptions = scribe.module('router/Viewer/native').options;
|
||||
|
||||
// update
|
||||
nativePackage.main = format(nativeOptions);
|
||||
|
||||
// save
|
||||
fs.writeFileSync(`${__dirname}/../../native/package.json`,
|
||||
JSON.stringify(nativePackage, null, 4), {encoding: 'utf8'});
|
||||
|
||||
const nw = new NwBuilder(extend(true, {
|
||||
platforms: ['win', 'osx', 'linux'],
|
||||
buildDir: `${__dirname}/../../public/native`,
|
||||
version: '0.12.3',
|
||||
zip: true
|
||||
}, nativeOptions, {files: `${__dirname}/../../native/**/**`}));
|
||||
|
||||
if (nativeOptions.debug) {
|
||||
nw.on('log', d => console.log(d));
|
||||
}
|
||||
|
||||
return nw.build();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,135 +0,0 @@
|
|||
import express, {Router} from 'express'
|
||||
import mongoose from 'mongoose'
|
||||
import EntrySchema from '../schemas/entry'
|
||||
import jade from 'jade'
|
||||
import basicAuth from 'basic-auth';
|
||||
import bodyParser from 'body-parser'
|
||||
|
||||
function getObject(d, def) {
|
||||
if (typeof d === 'undefined' || d === null) {
|
||||
return def || {};
|
||||
} else if (typeof d === 'object') {
|
||||
return d;
|
||||
} else {
|
||||
try {
|
||||
return JSON.parse(d);
|
||||
} catch (e) {
|
||||
return def || {};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const viewer = jade.compileFile(`${__dirname}/../../views/viewer.jade`);
|
||||
const login = jade.compileFile(`${__dirname}/../../views/login.jade`);
|
||||
|
||||
export function create(mongoUri = 'mongodb://localhost/scribe', routerConfig = {}, clientConfig = {}, debug = false) {
|
||||
routerConfig = Object.assign({
|
||||
useBodyParser: true,
|
||||
username: 'build',
|
||||
password: 'build'
|
||||
}, routerConfig);
|
||||
|
||||
let conn, Entry;
|
||||
|
||||
if (mongoUri) {
|
||||
mongoose.set('debug', debug);
|
||||
conn = mongoose.createConnection(mongoUri);
|
||||
Entry = conn.model('Entry', EntrySchema);
|
||||
}
|
||||
|
||||
const router = new Router();
|
||||
|
||||
var authenticate = function (req, res, next) {
|
||||
function unauthorized(res) {
|
||||
res.set('WWW-Authenticate', 'Basic realm=Authorization Required');
|
||||
return res.sendStatus(401);
|
||||
}
|
||||
|
||||
if (routerConfig.authentication === false || (!routerConfig.username && !routerConfig.password)) {
|
||||
return next();
|
||||
}
|
||||
|
||||
var user = basicAuth(req);
|
||||
|
||||
if (!user || !user.name || !user.pass) {
|
||||
return unauthorized(res);
|
||||
}
|
||||
|
||||
if (user.name === routerConfig.username && user.pass === routerConfig.password) {
|
||||
return next();
|
||||
} else {
|
||||
return unauthorized(res);
|
||||
}
|
||||
};
|
||||
|
||||
router.use(authenticate);
|
||||
router.use(express.static(`${__dirname}/../../public`));
|
||||
|
||||
if (routerConfig.useBodyParser) {
|
||||
router.use(bodyParser.json());
|
||||
}
|
||||
|
||||
const renderViewer = (req, res)=> res.send(viewer({config: JSON.stringify(clientConfig)}));
|
||||
|
||||
router.get('/', renderViewer);
|
||||
router.get('/viewer', renderViewer);
|
||||
|
||||
router.get('/rest/:collection', (req, res)=> {
|
||||
if (!mongoUri) {
|
||||
return res.json({err: 0, docs: []});
|
||||
}
|
||||
|
||||
var collection = req.params.collection;
|
||||
var selector = getObject(req.query.selector);
|
||||
var fields = typeof req.query.fields === 'string' ? req.query.fields : '';
|
||||
var sort = getObject(req.query.sort, {_id: -1});
|
||||
var limit = !isNaN(req.query.limit) ? Math.max(0, parseInt(Number(req.query.limit))) : Number.MAX_SAFE_INTEGER;
|
||||
var col = Entry; // defaulting to Entry for now
|
||||
|
||||
if (!col) {
|
||||
return res.json({err: 1, docs: []});
|
||||
}
|
||||
|
||||
col.find(selector)
|
||||
.select(fields)
|
||||
.sort(sort)
|
||||
.limit(limit)
|
||||
.lean()
|
||||
.exec((err = 0, docs = []) => res.json({err, docs}));
|
||||
});
|
||||
|
||||
router.delete('/rest/:collection', (req, res)=> {
|
||||
if (!mongoUri) {
|
||||
res.status(410);
|
||||
return res.send();
|
||||
}
|
||||
|
||||
var collection = rreq.params.collection;
|
||||
var ids = req.query.id;
|
||||
|
||||
try {
|
||||
ids = JSON.parse(ids);
|
||||
} catch (e) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
if (!Array.isArray(ids)) {
|
||||
ids = [req.param('id')];
|
||||
}
|
||||
|
||||
var col = Entry; // defaulting to Entry for now
|
||||
|
||||
if (col) {
|
||||
return col.remove({_id: {$in: ids}}, err => {
|
||||
res.status(err ? 410 : 200);
|
||||
res.send();
|
||||
});
|
||||
}
|
||||
|
||||
res.status(410);
|
||||
res.send();
|
||||
});
|
||||
|
||||
return router;
|
||||
}
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
import {Schema} from 'mongoose'
|
||||
|
||||
const EntrySchema = new Schema({
|
||||
transient: Schema.Types.Object,
|
||||
persistent: Schema.Types.Object,
|
||||
args: Schema.Types.Object,
|
||||
date: {type: Date, default: Date.now},
|
||||
expose: String,
|
||||
serialized: String // experimental
|
||||
});
|
||||
|
||||
EntrySchema.index({'persistent.app': 1, 'persistent.id': -1});
|
||||
|
||||
export default EntrySchema;
|
174
src/scribe.js
174
src/scribe.js
|
@ -1,136 +1,64 @@
|
|||
import fs from 'fs'
|
||||
import path from 'path'
|
||||
import Console from './readers/Console'
|
||||
import BasicConsole from './readers/BasicConsole'
|
||||
import Inspector from './transforms/Inspector'
|
||||
import ExpressInspector from './transforms/ExpressInspector'
|
||||
import ExpressExtractor from './transforms/ExpressExtractor'
|
||||
import JSON2Converter from './transforms/JSON2Converter'
|
||||
import ErrorExtractor from './transforms/ErrorExtractor'
|
||||
import MongoDB from './writers/MongoDB'
|
||||
import SocketIO from './writers/SocketIO'
|
||||
import DefaultConsole from './writers/DefaultConsole'
|
||||
import {create} from './routers/viewer'
|
||||
import NwBuilder from 'nw-builder'
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import rc from 'rc'
|
||||
import nativePackage from './../native/package.json'
|
||||
import extend from 'extend'
|
||||
import extend from 'extend';
|
||||
|
||||
export const Writer = {MongoDB, DefaultConsole};
|
||||
export const Reader = {BasicConsole, Console};
|
||||
export const Transform = {Inspector, ExpressInspector, ExpressExtractor, ErrorExtractor, JSON2Converter};
|
||||
import * as Middleware from './middleware';
|
||||
import * as Reader from './reader';
|
||||
import * as Router from './router';
|
||||
import * as Transform from './transform';
|
||||
import * as Writer from './writer';
|
||||
|
||||
const defaultOpts = {
|
||||
name: 'Scribe',
|
||||
mongoUri: 'mongodb://localhost/scribe',
|
||||
mongo: true,
|
||||
basePath: 'scribe/',
|
||||
socketPort: 4000,
|
||||
socket: true,
|
||||
inspector: {
|
||||
colors: true,
|
||||
showHidden: false,
|
||||
depth: 5,
|
||||
pre: true,
|
||||
callsite: true,
|
||||
tags: true,
|
||||
args: true,
|
||||
metrics: true
|
||||
},
|
||||
web: {
|
||||
router: {
|
||||
username: 'build',
|
||||
password: 'build',
|
||||
authentication: true,
|
||||
sessionSecret: 'scribe-session',
|
||||
useBodyParser: true,
|
||||
useSession: true
|
||||
},
|
||||
client: {
|
||||
socketPorts: [4000],
|
||||
exposed: {
|
||||
all: {label: 'all', query: {expose: {$exists: true}}},
|
||||
//error: {label: 'error', query: {expose: 'error'}},
|
||||
//express: {label: 'express', query: {expose: 'express'}},
|
||||
//info: {label: 'info', query: {expose: 'info'}},
|
||||
//log: {label: 'log', query: {expose: 'log'}},
|
||||
//warn: {label: 'warn', query: {expose: 'warn'}},
|
||||
//trace: {label: 'trace', query: {expose: 'trace'}}
|
||||
}
|
||||
export {Middleware};
|
||||
export {Reader};
|
||||
export {Router};
|
||||
export {Transform};
|
||||
export {Writer};
|
||||
|
||||
export function resolvePipeline(scribe, pipeline) {
|
||||
const resolved = [];
|
||||
for (const through of pipeline) {
|
||||
if (typeof through === 'function') {
|
||||
resolved.push(new through(scribe));
|
||||
} else if (typeof through === 'object') {
|
||||
resolved.push(through);
|
||||
} else if (typeof through === 'string') {
|
||||
const Class = require(path.join(__dirname, through)).default;
|
||||
resolved.push(new Class(scribe));
|
||||
}
|
||||
},
|
||||
nwjs: {},
|
||||
debug: false
|
||||
};
|
||||
|
||||
export default function (id = process.pid, opts = rc('scribe', defaultOpts), ...exposers) {
|
||||
opts = extend(true, {}, defaultOpts, opts);
|
||||
|
||||
var console = new BasicConsole(opts.name, id || opts.instanceId);
|
||||
|
||||
function appendTransforms(args) {
|
||||
if (opts.mongo && opts.mongoUri && opts.socket && opts.socketPort) {
|
||||
args.push(new JSON2Converter());
|
||||
args.push(new MongoDB(opts.mongoUri, opts.debug));
|
||||
args.push(new SocketIO(opts.socketPort, opts.debug));
|
||||
} else if (opts.mongo && opts.mongoUri) {
|
||||
args.push(new JSON2Converter());
|
||||
args.push(new MongoDB(opts.mongoUri, opts.debug));
|
||||
} else if (opts.socket && opts.socketPort) {
|
||||
args.push(new JSON2Converter());
|
||||
args.push(new SocketIO(opts.socketPort, opts.debug));
|
||||
}
|
||||
|
||||
return args;
|
||||
}
|
||||
|
||||
console.exposed().concat(exposers).forEach(expose => {
|
||||
console.expose(expose);
|
||||
return resolved;
|
||||
}
|
||||
|
||||
let args = appendTransforms([expose, 'mongo-socket', new ErrorExtractor()]);
|
||||
export function create(opts) {
|
||||
opts = extend(true, rc('scribe', {}), opts);
|
||||
|
||||
console.pipe.apply(console, args);
|
||||
// create default console
|
||||
const console = new Reader.BasicConsole(opts);
|
||||
const {expose: exposeMap, 'expose/pipeline': pipelineMap} = opts;
|
||||
|
||||
console.pipe(expose, 'bash',
|
||||
new Inspector(opts.inspector),
|
||||
new DefaultConsole());
|
||||
});
|
||||
[...console.exposed(), ...Object.keys(exposeMap)]
|
||||
.forEach(expose => {
|
||||
if (expose === 'default') return;
|
||||
const pipelines = exposeMap[expose] || exposeMap.default;
|
||||
if (Array.isArray(pipelines)) {
|
||||
pipelines.forEach(pipeline => {
|
||||
if (Array.isArray(pipelineMap[pipeline])) {
|
||||
if (opts.debug) {
|
||||
process.stdout.write(`Exposing ${expose} through ${pipeline}\n`);
|
||||
}
|
||||
|
||||
let args = appendTransforms(['express', 'mongo-socket', new ErrorExtractor(), new ExpressExtractor()]);
|
||||
console.expose(expose);
|
||||
console.pipe(expose, pipeline, ...resolvePipeline(console, pipelineMap[pipeline]));
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
console.pipe.apply(console, args);
|
||||
|
||||
console.pipe('express', 'bash',
|
||||
new ExpressExtractor(),
|
||||
new ExpressInspector(),
|
||||
new Inspector(opts.inspector),
|
||||
new DefaultConsole());
|
||||
|
||||
console.viewer = create.bind(null, opts.mongo && opts.mongoUri, opts.web.router, opts.web.client, opts.debug);
|
||||
|
||||
console.build = ()=> {
|
||||
|
||||
// update
|
||||
nativePackage.main = `${opts.publicUri}:${path.join(String(opts.web.client.port), opts.basePath)}`;
|
||||
|
||||
// save
|
||||
fs.writeFileSync(`${__dirname}/../native/package.json`, JSON.stringify(nativePackage, null, 4), {encoding: 'utf8'});
|
||||
|
||||
const nw = new NwBuilder(extend(true, {
|
||||
platforms: ['win', 'osx', 'linux'],
|
||||
buildDir: `${__dirname}/../public/native`,
|
||||
version: '0.12.3',
|
||||
zip: true
|
||||
}, opts.nwjs, {files: `${__dirname}/../native/**/**`}));
|
||||
|
||||
if (opts.debug) {
|
||||
nw.on('log', d => console.log(d));
|
||||
}
|
||||
|
||||
return nw.build();
|
||||
};
|
||||
|
||||
process.on('uncaughtException', e => console.error(e).then(() => process.exit(1)));
|
||||
if (opts.handleUncaughtException) {
|
||||
process.on('uncaughtException', e => console.error(e).then(() => process.exit(1)));
|
||||
}
|
||||
|
||||
return console;
|
||||
};
|
||||
}
|
||||
|
|
13
src/transform/ErrorExtractor.js
Normal file
13
src/transform/ErrorExtractor.js
Normal file
|
@ -0,0 +1,13 @@
|
|||
export default class ErrorExtractor {
|
||||
through(data, callback) {
|
||||
data.args =
|
||||
data.args.map(a =>
|
||||
a instanceof Error ?
|
||||
a.toJSON ?
|
||||
a.toJSON() :
|
||||
JSON.parse(JSON.stringify(a, ["message", "arguments", "type", "name", "stack", "code"]))
|
||||
: a);
|
||||
|
||||
callback(null, data);
|
||||
}
|
||||
}
|
|
@ -4,11 +4,11 @@ import auth from 'basic-auth'
|
|||
export default class ExpressExtractor {
|
||||
ip(req) {
|
||||
return req.ip
|
||||
|| req._remoteAddress
|
||||
|| req.remoteAddress
|
||||
|| (req.connection && req.connection.remoteAddress)
|
||||
|| req.headers['x-forwarded-for']
|
||||
|| undefined;
|
||||
|| req._remoteAddress
|
||||
|| req.remoteAddress
|
||||
|| (req.connection && req.connection.remoteAddress)
|
||||
|| req.headers['x-forwarded-for']
|
||||
|| 'xx.x.x.xx';
|
||||
}
|
||||
|
||||
header(res, field) {
|
||||
|
@ -19,8 +19,8 @@ export default class ExpressExtractor {
|
|||
var header = res.getHeader(field);
|
||||
|
||||
return Array.isArray(header)
|
||||
? header.join(', ')
|
||||
: header
|
||||
? header.join(', ')
|
||||
: header
|
||||
}
|
||||
|
||||
remoteUser(req) {
|
|
@ -38,7 +38,7 @@ export default class ExpressInspector {
|
|||
const express = data.args[0];
|
||||
|
||||
data.transient['callsite'] = `${express.ip}`;
|
||||
data.args = [`${chalk.bgGreen.black(` ${express.method} `)} ${chalk.gray(express.url)} ${this.status(express)} - ${express.contentLength} ${chalk.gray(`(${express.duration.toFixed(3)}ms)`)}`];
|
||||
data.args = [`${chalk.bgGreen.black(` ${express.method} `)} ${chalk.gray(express.originalUrl)} ${this.status(express)} - ${express.contentLength} ${chalk.gray(`(${express.duration.toFixed(3)}ms)`)}`];
|
||||
|
||||
callback(null, data);
|
||||
}
|
8
src/transform/FullTextSerialize.js
Normal file
8
src/transform/FullTextSerialize.js
Normal file
|
@ -0,0 +1,8 @@
|
|||
import * as JSON2 from '../libs/JSON2'
|
||||
|
||||
export default class FullTextSerialize {
|
||||
through(data, callback) {
|
||||
data.serialized = JSON2.stringify(data);
|
||||
callback(null, data);
|
||||
}
|
||||
}
|
|
@ -7,13 +7,12 @@ function inspect(x, ctx) {
|
|||
}
|
||||
|
||||
export default class Inspector {
|
||||
constructor(inspectOpts = {colors: true, showHidden: false,
|
||||
depth: 5, pre: true, args: true, metrics: true, tags: true}) {
|
||||
this.inspectOpts = inspectOpts;
|
||||
constructor(scribe) {
|
||||
this.options = scribe.module('transform/Inspector').options;
|
||||
}
|
||||
|
||||
format(f, ...args) {
|
||||
const ctx = this.inspectOpts;
|
||||
const ctx = this.options;
|
||||
|
||||
if (typeof f !== 'string') {
|
||||
var objects = [];
|
||||
|
@ -55,7 +54,7 @@ export default class Inspector {
|
|||
|
||||
inspectTags(data) {
|
||||
return ((data.persistent['tags'] || []).map(tag => chalk.cyan(`${String(tag).toUpperCase()}`)))
|
||||
.concat((data.transient['tags'] || []).map(tag => chalk.magenta(`${String(tag).toUpperCase()}`))).join(' ');
|
||||
.concat((data.transient['tags'] || []).map(tag => chalk.magenta(`${String(tag).toUpperCase()}`))).join(' ');
|
||||
}
|
||||
|
||||
inspectPre(data) {
|
||||
|
@ -78,11 +77,11 @@ export default class Inspector {
|
|||
data.args = '';
|
||||
}
|
||||
|
||||
const pre = this.inspectOpts.pre ? `${this.inspectPre(data)} ` : '';
|
||||
const tags = this.inspectOpts.tags ? this.inspectTags(data) : '';
|
||||
const metrics = this.inspectOpts.metrics ? this.inspectMetrics(data) : '';
|
||||
const site = this.inspectOpts.callsite ? this.inspectCallSite(data) : '';
|
||||
const pretty = this.inspectOpts.args ? this.inspectArguments(data) : '';
|
||||
const pre = this.options.pre ? `${this.inspectPre(data)} ` : '';
|
||||
const tags = this.options.tags ? this.inspectTags(data) : '';
|
||||
const metrics = this.options.metrics ? this.inspectMetrics(data) : '';
|
||||
const site = this.options.callsite ? this.inspectCallSite(data) : '';
|
||||
const pretty = this.options.args ? this.inspectArguments(data) : '';
|
||||
const inspected = pretty.split('\n').map(line =>`${pre} ${[tags, metrics, line, site].join(' ')}`);
|
||||
|
||||
data.inspected = inspected;
|
|
@ -1,6 +1,6 @@
|
|||
import * as JSON2 from '../libs/JSON2'
|
||||
|
||||
export default class JSON2Converter {
|
||||
export default class ToJSON2 {
|
||||
through(data, callback) {
|
||||
callback(null, JSON.parse(JSON2.stringify(data)));
|
||||
}
|
5
src/transform/index.js
Normal file
5
src/transform/index.js
Normal file
|
@ -0,0 +1,5 @@
|
|||
export {default as ErrorExtractor} from './ErrorExtractor';
|
||||
export {default as ExpressExtractor} from './ExpressExtractor';
|
||||
export {default as ExpressInspector} from './ExpressInspector';
|
||||
export {default as Inspector} from './Inspector';
|
||||
export {default as ToJSON2} from './ToJSON2';
|
|
@ -1,8 +0,0 @@
|
|||
export default class ErrorExtractor {
|
||||
through(data, callback) {
|
||||
data.args = data.args.map(a => a instanceof Error ?
|
||||
JSON.parse(JSON.stringify(a, ["message", "arguments", "type", "name", "stack"])) : a);
|
||||
|
||||
callback(null, data);
|
||||
}
|
||||
}
|
41
src/writer/MongoDB.js
Normal file
41
src/writer/MongoDB.js
Normal file
|
@ -0,0 +1,41 @@
|
|||
import mongoose, {Schema} from 'mongoose';
|
||||
import * as JSON2 from '../libs/JSON2';
|
||||
import express, {Router} from 'express';
|
||||
import jade from 'jade';
|
||||
import basicAuth from 'basic-auth-connect';
|
||||
import bodyParser from 'body-parser'
|
||||
import JSONStream from 'JSONStream';
|
||||
|
||||
export const EntrySchema = new Schema({
|
||||
transient: Schema.Types.Object,
|
||||
persistent: Schema.Types.Object,
|
||||
args: Schema.Types.Object,
|
||||
date: {type: Date, default: Date.now},
|
||||
expose: String,
|
||||
serialized: String
|
||||
});
|
||||
|
||||
EntrySchema.index({'persistent.app': 1, 'persistent.id': -1});
|
||||
EntrySchema.index({'serialized': 1, 'comment': 'text'});
|
||||
|
||||
export default class MongoDB {
|
||||
constructor(scribe) {
|
||||
this.options = scribe.module('writer/MongoDB').options;
|
||||
|
||||
const conn = mongoose.createConnection(this.options.uri);
|
||||
this._debug = this.options.debug;
|
||||
this._entry = conn.model('Entry', EntrySchema);
|
||||
}
|
||||
|
||||
get Entry() {
|
||||
return this._entry;
|
||||
}
|
||||
|
||||
through(data, callback) {
|
||||
new this.Entry(data).save((err, data) => {
|
||||
if (err && this._debug) console.error(err);
|
||||
callback(err, data);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -3,11 +3,11 @@ import io from 'socket.io'
|
|||
let sio;
|
||||
|
||||
export default class SocketIO {
|
||||
constructor(port = 4000, debug = false) {
|
||||
process.env.DEBUG = debug && 'socket.io*';
|
||||
constructor(scribe) {
|
||||
this.options = scribe.module('writer/SocketIO').options;
|
||||
|
||||
if (!sio) {
|
||||
sio = io(port);
|
||||
sio = io(this.options.port, this.options.options);
|
||||
}
|
||||
}
|
||||
|
3
src/writer/index.js
Normal file
3
src/writer/index.js
Normal file
|
@ -0,0 +1,3 @@
|
|||
export {default as DefaultConsole} from './DefaultConsole';
|
||||
export {default as MongoDB} from './MongoDB';
|
||||
export {default as SocketIO} from './SocketIO'
|
|
@ -1,24 +0,0 @@
|
|||
import mongoose from 'mongoose'
|
||||
import EntrySchema from '../schemas/entry'
|
||||
import * as JSON2 from '../libs/JSON2'
|
||||
|
||||
export default class MongoDB {
|
||||
constructor(address = 'mongodb://localhost/scribe', debug = false) {
|
||||
mongoose.set('debug', debug);
|
||||
|
||||
const conn = mongoose.createConnection(address);
|
||||
this._debug = debug;
|
||||
this._entry = conn.model('Entry', EntrySchema);
|
||||
}
|
||||
|
||||
get Entry() {
|
||||
return this._entry;
|
||||
}
|
||||
|
||||
through(data, callback) {
|
||||
new this.Entry(data).save((err, data) => {
|
||||
if (err && this._debug) console.error(err);
|
||||
callback(err, data);
|
||||
});
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
import Scribe from '../'
|
||||
import * as Scribe from '../'
|
||||
import express from 'express'
|
||||
import expect from 'expect.js'
|
||||
import request from 'superagent'
|
||||
|
@ -8,7 +8,7 @@ describe('Basic Scribe', ()=> {
|
|||
|
||||
const port = 3008;
|
||||
const requestsCount = 10;
|
||||
const console = new Scribe();
|
||||
const console = Scribe.create();
|
||||
|
||||
console.persistent('tags', ['mocha', 'scribe']);
|
||||
|
||||
|
@ -16,8 +16,9 @@ describe('Basic Scribe', ()=> {
|
|||
|
||||
const app = express();
|
||||
let count = 0, did404 = 0;
|
||||
const logger = new Scribe.Middleware.ExpressLogger(console);
|
||||
|
||||
app.use(console.middleware('express'));
|
||||
app.use(logger.getMiddleware());
|
||||
app.get('/test', (req, res) => {
|
||||
if (did404) {
|
||||
return res.json({test: new Array(parseInt(Math.random() * 50)).join('.')});
|
||||
|
@ -35,16 +36,16 @@ describe('Basic Scribe', ()=> {
|
|||
count++;
|
||||
|
||||
request
|
||||
.get(`http://localhost:${port}/test`)
|
||||
.end((err, req)=> {
|
||||
if (err) {
|
||||
return callback();
|
||||
} else {
|
||||
console
|
||||
.log(`Received response ${count}/${requestsCount}`, req.body)
|
||||
.then(()=>callback());
|
||||
}
|
||||
});
|
||||
.get(`http://localhost:${port}/test`)
|
||||
.end((err, req)=> {
|
||||
if (err) {
|
||||
return callback();
|
||||
} else {
|
||||
console
|
||||
.log(`Received response ${count}/${requestsCount}`, req.body)
|
||||
.then(()=>callback());
|
||||
}
|
||||
});
|
||||
}, () => {
|
||||
expect(count).to.equal(requestsCount);
|
||||
done();
|
||||
|
|
|
@ -1,80 +1,65 @@
|
|||
import Scribe from '../'
|
||||
import * as Scribe from '../'
|
||||
import expect from 'expect.js'
|
||||
import TraceError from 'trace-error';
|
||||
|
||||
describe('Basic Scribe', ()=> {
|
||||
|
||||
const console = new Scribe();
|
||||
const console = Scribe.create();
|
||||
|
||||
console.persistent('tags', ['mocha', 'scribe']);
|
||||
|
||||
it('should print objects to terminal', done => {
|
||||
Promise.all([
|
||||
console.tag('object').log('Inspect object', {test: true}),
|
||||
console.tag('object').log('Inspect object', console),
|
||||
console.log()
|
||||
]).then(() => done());
|
||||
it('should print objects to terminal', async () => {
|
||||
await console.tag('object').log('Inspect object', {test: true});
|
||||
await console.tag('object').log('Inspect object', console);
|
||||
await console.log();
|
||||
});
|
||||
|
||||
it('should print functions to terminal', done => {
|
||||
Promise.all([
|
||||
console.tag('function').log('Inspect function', Function)
|
||||
]).then(() => done());
|
||||
it('should print functions to terminal', async () => {
|
||||
await console.tag('function').log('Inspect function', Function);
|
||||
});
|
||||
|
||||
it('should print metrics to terminal', done => {
|
||||
Promise.all([
|
||||
console.tag('metric').metric('appTime', 500, 'dbTime', 750).log('Inspect metric'),
|
||||
console.tag('metric').metric('appTime', 500, 'dbTime', 750).log()
|
||||
]).then(() => done());
|
||||
it('should print metrics to terminal', async () => {
|
||||
await console.tag('metric').metric('appTime', 500, 'dbTime', 750).log('Inspect metric');
|
||||
await console.tag('metric').metric('appTime', 500, 'dbTime', 750).log()
|
||||
});
|
||||
|
||||
it('should print errors to terminal', done => {
|
||||
Promise.all([
|
||||
console.tag('error').error(new Error('Scribe error test 1')),
|
||||
console.tag('error').error(new Error('Scribe error test 2'))
|
||||
]).then(() => done());
|
||||
it('should print errors to terminal', async () => {
|
||||
await console.tag('error').error(new Error('Scribe error test 1'));
|
||||
await console.tag('error').error(new Error('Scribe error test 2'));
|
||||
await console.tag('error').error(new TraceError('Scribe error test 2', new Error("Sub error")));
|
||||
await console.tag('error').error(JSON.stringify(new TraceError('test error', new Error("Sub error")),
|
||||
["message", "arguments", "type", "name", "stack"]));
|
||||
});
|
||||
|
||||
it('should print dates to terminal', done => {
|
||||
Promise.all([
|
||||
console.tag('date').log(new Date())
|
||||
]).then(() => done());
|
||||
it('should print dates to terminal', async () => {
|
||||
await console.tag('date').log(new Date());
|
||||
});
|
||||
|
||||
it('should print booleans to terminal', done => {
|
||||
Promise.all([
|
||||
console.tag('boolean').log(true)
|
||||
]).then(() => done());
|
||||
it('should print booleans to terminal', async () => {
|
||||
await console.tag('boolean').log(true);
|
||||
});
|
||||
|
||||
it('should print maps to terminal', done => {
|
||||
it('should print maps to terminal', async () => {
|
||||
let map0 = new Map(), map1 = new WeakMap();
|
||||
map0.set('scribe', 'test');
|
||||
map1.set({}, 'test');
|
||||
|
||||
Promise.all([
|
||||
console.tag('map').log(map0),
|
||||
console.tag('map').log(map1)
|
||||
]).then(() => done());
|
||||
await console.tag('map').log(map0);
|
||||
await console.tag('map').log(map1);
|
||||
});
|
||||
|
||||
it('should print sets to terminal', done => {
|
||||
it('should print sets to terminal', async () => {
|
||||
let set0 = new Set(), set1 = new WeakSet();
|
||||
set0.add('scribe');
|
||||
set1.add({});
|
||||
|
||||
Promise.all([
|
||||
console.tag('set').log(set0),
|
||||
console.tag('set').log(set1)
|
||||
]).then(() => done());
|
||||
await console.tag('set').log(set0);
|
||||
await console.tag('set').log(set1);
|
||||
});
|
||||
|
||||
it('should print promises to terminal', done => {
|
||||
it('should print promises to terminal', async () => {
|
||||
const promise = new Promise(()=> 0);
|
||||
|
||||
Promise.all([
|
||||
console.tag('promise').log(promise, promise instanceof Promise)
|
||||
]).then(() => done());
|
||||
await console.tag('promise').log(promise, promise instanceof Promise);
|
||||
});
|
||||
|
||||
});
|
|
@ -1,6 +1,7 @@
|
|||
doctype html
|
||||
html
|
||||
head
|
||||
title Scribe.js
|
||||
meta(charset="UTF-8")
|
||||
link(rel='stylesheet', type='text/css', href='styles/viewer.min.css')
|
||||
script.
|
||||
|
|
Loading…
Add table
Reference in a new issue