mirror of
https://github.com/mgerb/mywebsite
synced 2026-01-11 18:32:50 +00:00
@@ -1,4 +1,4 @@
|
|||||||
var width, height, largeHeader, canvas, ctx, circles, target, animateHeader = true;
|
let width, height, largeHeader, canvas, ctx, circles, target, animateHeader = true;
|
||||||
|
|
||||||
// Main
|
// Main
|
||||||
export function bubble() {
|
export function bubble() {
|
||||||
|
|||||||
@@ -1,19 +1,19 @@
|
|||||||
.Content {
|
.Content {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
.post + .post {
|
min-width: 0;
|
||||||
|
.post+ .post {
|
||||||
margin-top: 2em;
|
margin-top: 2em;
|
||||||
}
|
}
|
||||||
.date {
|
.date {
|
||||||
display: block;
|
display: block;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
opacity: 0.7;
|
opacity: 0.7;
|
||||||
|
@media (min-width: 1200px) {
|
||||||
@media (min-width: 1200px){
|
position: absolute;
|
||||||
position: absolute;
|
width: 7em;
|
||||||
width: 7em;
|
text-align: right;
|
||||||
text-align: right;
|
margin-left: -8em;
|
||||||
margin-left: -8em;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.intro {
|
.intro {
|
||||||
@@ -28,4 +28,7 @@
|
|||||||
pre {
|
pre {
|
||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
}
|
}
|
||||||
|
.btn {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ $transitionDuration: 0.4s;
|
|||||||
&:before {
|
&:before {
|
||||||
content: " ";
|
content: " ";
|
||||||
display: block;
|
display: block;
|
||||||
background: rgba(0,0,0,0.4);
|
background: rgba(0, 0, 0, 0.4);
|
||||||
position: absolute;
|
position: absolute;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100vw;
|
width: 100vw;
|
||||||
|
|||||||
@@ -1,20 +1,23 @@
|
|||||||
@import './utils.scss';
|
@import './utils.scss';
|
||||||
|
|
||||||
$linkColor: #3598db;
|
$linkColor: #3598db;
|
||||||
|
$backgroundColor: #FFFFFF;
|
||||||
html {
|
html {
|
||||||
font-family: 'Roboto Slab', serif;
|
font-family: 'Roboto Slab', serif;
|
||||||
max-width: 100%;
|
overflow: auto;
|
||||||
overflow-x: hidden;
|
|
||||||
overflow-y: scroll;
|
@media (max-width: 768px) {
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
max-width: 100%;
|
overflow: hidden;
|
||||||
overflow-x: hidden;
|
background-color: $backgroundColor;
|
||||||
position: relative;
|
position: relative;
|
||||||
-webkit-font-smoothing: subpixel-antialiased;
|
-webkit-font-smoothing: antialiased;
|
||||||
font-weight: 300;
|
font-weight: 300;
|
||||||
}
|
}
|
||||||
|
|
||||||
h1,
|
h1,
|
||||||
h2,
|
h2,
|
||||||
h3,
|
h3,
|
||||||
@@ -25,9 +28,11 @@ h6 {
|
|||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
line-height: 1em;
|
line-height: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
p {
|
p {
|
||||||
font-weight: 300;
|
font-weight: 300;
|
||||||
}
|
}
|
||||||
|
|
||||||
*,
|
*,
|
||||||
*:after,
|
*:after,
|
||||||
*:before {
|
*:before {
|
||||||
@@ -35,6 +40,7 @@ p {
|
|||||||
-moz-box-sizing: border-box;
|
-moz-box-sizing: border-box;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
a {
|
a {
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
color: $linkColor;
|
color: $linkColor;
|
||||||
@@ -43,11 +49,13 @@ a {
|
|||||||
color: $linkColor;
|
color: $linkColor;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
hr {
|
hr {
|
||||||
margin: 1em;
|
margin: 1em;
|
||||||
border: 0;
|
border: 0;
|
||||||
border-top: 1px solid #eee;
|
border-top: 1px solid #eee;
|
||||||
}
|
}
|
||||||
|
|
||||||
.Footer,
|
.Footer,
|
||||||
.Main {
|
.Main {
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -59,9 +67,11 @@ hr {
|
|||||||
width: 1em;
|
width: 1em;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.Main {
|
.Main {
|
||||||
padding-top: 1em;
|
padding-top: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.Header {
|
.Header {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
background: url("../images/header.jpg");
|
background: url("../images/header.jpg");
|
||||||
@@ -74,8 +84,20 @@ hr {
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.Loading{
|
|
||||||
|
.Loading {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.btn {
|
||||||
|
background-color: $backgroundColor;
|
||||||
|
border: 1px solid #DADADA;
|
||||||
|
border-radius: .2em;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: .5em;
|
||||||
|
&:hover {
|
||||||
|
background-color: #F3F3F3;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,39 +9,38 @@ import {connect, Provider} from 'react-redux';
|
|||||||
import store, {history} from './redux/store';
|
import store, {history} from './redux/store';
|
||||||
|
|
||||||
//import actions
|
//import actions
|
||||||
import * as actions from './redux/actions';
|
import * as appActions from './redux/actions/app';
|
||||||
|
import * as sensorActions from './redux/actions/sensor';
|
||||||
|
|
||||||
import Index from './pages/Index';
|
import Index from './pages/Index';
|
||||||
|
import Preview from './components/Preview';
|
||||||
|
import Post from './components/Post';
|
||||||
|
import SensorInfo from './components/sensors/SensorInfo';
|
||||||
|
|
||||||
class Main extends React.Component{
|
function mapStateToProps(state) {
|
||||||
render(){
|
|
||||||
return(
|
|
||||||
<div>{React.cloneElement(this.props.children, this.props)}</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function mapStateToProps(state){
|
|
||||||
return {
|
return {
|
||||||
redux: state.reducer
|
app: state.app,
|
||||||
|
sensor: state.sensor
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function mapDispatchToProps(dispatch){
|
function mapDispatchToProps(dispatch) {
|
||||||
return{
|
return {
|
||||||
actions: bindActionCreators(actions, dispatch)
|
appActions: bindActionCreators(appActions, dispatch),
|
||||||
|
sensorActions: bindActionCreators(sensorActions, dispatch)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const App = connect(mapStateToProps, mapDispatchToProps)(Main);
|
const App = connect(mapStateToProps, mapDispatchToProps)(Index);
|
||||||
|
|
||||||
ReactDOM.render((
|
ReactDOM.render((
|
||||||
<Provider store={store}>
|
<Provider store={store}>
|
||||||
<Router history={history}>
|
<Router history={history}>
|
||||||
<Route path="/" component={App}>
|
<Route path="/" component={App}>
|
||||||
<IndexRoute component={Index}/>
|
<IndexRoute component={Preview}/>
|
||||||
<Route path="/:page(/:category)(/:post)" component={Index}/>
|
<Route path="post/:category/:post" component={Post}/>
|
||||||
|
<Route path="sensor/:location/:year/:month" component={SensorInfo}/>
|
||||||
</Route>
|
</Route>
|
||||||
</Router>
|
</Router>
|
||||||
</Provider>
|
</Provider>
|
||||||
),document.getElementById('app'));
|
), document.getElementById('app'));
|
||||||
|
|||||||
@@ -2,10 +2,10 @@ import React from 'react';
|
|||||||
|
|
||||||
import '../../assets/scss/Footer.scss';
|
import '../../assets/scss/Footer.scss';
|
||||||
|
|
||||||
export default class Footer extends React.Component{
|
export default class Footer extends React.Component {
|
||||||
|
|
||||||
render(){
|
render() {
|
||||||
return(
|
return (
|
||||||
<div class="Footer">
|
<div class="Footer">
|
||||||
Site created and maintained by Mitchell Gerber
|
Site created and maintained by Mitchell Gerber
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {bubble} from '../../assets/js/bubble';
|
import {bubble} from '../../assets/js/bubble';
|
||||||
|
|
||||||
export default class Header extends React.Component{
|
export default class Header extends React.Component {
|
||||||
componentDidMount(){
|
componentDidMount() {
|
||||||
bubble();
|
bubble();
|
||||||
}
|
}
|
||||||
|
|
||||||
render(){
|
render() {
|
||||||
return(
|
return (
|
||||||
<header id="header" class="Header">
|
<header id="header" class="Header">
|
||||||
<canvas id="canvas" width="854" height="709"></canvas>
|
<canvas id="canvas" width="854" height="709"></canvas>
|
||||||
</header>
|
</header>
|
||||||
|
|||||||
@@ -1,28 +1,43 @@
|
|||||||
|
import hljs from 'highlight.js';
|
||||||
|
import marked from 'marked';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {Link} from 'react-router';
|
import {Link} from 'react-router';
|
||||||
import marked from 'marked';
|
|
||||||
import hljs from 'highlight.js';
|
//components
|
||||||
|
import Loading from './utils/Loading';
|
||||||
|
|
||||||
import '../../assets/scss/Content.scss';
|
import '../../assets/scss/Content.scss';
|
||||||
|
|
||||||
const renderer = new marked.Renderer();
|
const renderer = new marked.Renderer();
|
||||||
|
|
||||||
marked.setOptions({
|
marked.setOptions({
|
||||||
langPrefix: 'hljs ',
|
langPrefix: 'hljs ',
|
||||||
highlight: (code) => {
|
highlight: (code) => {
|
||||||
return hljs.highlightAuto(code).value;
|
return hljs.highlightAuto(code).value;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
export default class Post extends React.Component{
|
export default class Post extends React.Component {
|
||||||
|
|
||||||
render(){
|
componentDidMount() {
|
||||||
return(
|
const params = this.props.params;
|
||||||
|
this.props.appActions.fetchPost(params.category, params.post);
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const post = this.props.app.post;
|
||||||
|
const fetched = this.props.app.fetched;
|
||||||
|
const fetching = this.props.app.fetching;
|
||||||
|
|
||||||
|
return (
|
||||||
<div class="Content">
|
<div class="Content">
|
||||||
<div dangerouslySetInnerHTML={{__html : marked(this.props.content, {renderer : renderer})}}>
|
{fetched ?
|
||||||
|
<div>
|
||||||
|
<div dangerouslySetInnerHTML={{__html : marked(post, {renderer : renderer})}}/>
|
||||||
|
<Link to="/" class="link"><i class="fa fa-caret-left" aria-hidden="true"></i> Home</Link>
|
||||||
|
</div>
|
||||||
|
: <Loading/>}
|
||||||
</div>
|
</div>
|
||||||
<Link to="/" class="link"><i class="fa fa-caret-left" aria-hidden="true"></i> Home</Link>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,20 +1,26 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {Link} from 'react-router';
|
import {Link} from 'react-router';
|
||||||
|
|
||||||
|
//components
|
||||||
|
import Loading from './utils/Loading';
|
||||||
|
|
||||||
import '../../assets/scss/Content.scss';
|
import '../../assets/scss/Content.scss';
|
||||||
|
|
||||||
export default class Preview extends React.Component{
|
export default class Preview extends React.Component {
|
||||||
|
|
||||||
insertPosts(posts){
|
componentDidMount() {
|
||||||
|
this.props.appActions.fetchPreview();
|
||||||
|
}
|
||||||
|
|
||||||
|
insertPosts(posts) {
|
||||||
let elements = [];
|
let elements = [];
|
||||||
for (let i in posts){
|
for (let i = 0; i < this.props.app.postLimit && i < posts.length; i++) {
|
||||||
elements.push(
|
elements.push(
|
||||||
<div class="post" key={i}>
|
<div class="post" key={i}>
|
||||||
<div class="date">
|
<div class="date">
|
||||||
{posts[i].date}
|
{posts[i].date}
|
||||||
</div>
|
</div>
|
||||||
<h1 class="intro" >{posts[i].title.toString()}</h1>
|
<div dangerouslySetInnerHTML={{__html : posts[i].title.toString() + posts[i].intro.toString()}} />
|
||||||
<p>{posts[i].intro.toString()}</p>
|
|
||||||
<p>
|
<p>
|
||||||
<Link class="link" to={`/post/${posts[i].category}/${posts[i].filename}`}>
|
<Link class="link" to={`/post/${posts[i].category}/${posts[i].filename}`}>
|
||||||
continue reading <i class="fa fa-caret-right" aria-hidden="true"></i>
|
continue reading <i class="fa fa-caret-right" aria-hidden="true"></i>
|
||||||
@@ -27,12 +33,22 @@ export default class Preview extends React.Component{
|
|||||||
return elements;
|
return elements;
|
||||||
}
|
}
|
||||||
|
|
||||||
render(){
|
render() {
|
||||||
const posts = this.props.posts;
|
const posts = this.props.app.preview.posts;
|
||||||
|
const postLimit = this.props.app.postLimit;
|
||||||
|
const fetched = this.props.app.fetched;
|
||||||
|
const increasePostLimit = this.props.appActions.increasePostLimit;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div class="Content">
|
<div class="Content">
|
||||||
{posts.length > 0 ? this.insertPosts(posts): ""}
|
{fetched ?
|
||||||
|
<div>
|
||||||
|
{posts.length > 0 ? this.insertPosts(posts): null}
|
||||||
|
{posts.length > postLimit ?
|
||||||
|
<button class="btn" onClick={increasePostLimit}>Load More</button>
|
||||||
|
: null}
|
||||||
|
</div>
|
||||||
|
: <Loading/>}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,9 +7,9 @@ import SensorList from './sensors/SensorList';
|
|||||||
import me from '../../assets/images/me.jpg';
|
import me from '../../assets/images/me.jpg';
|
||||||
import '../../assets/scss/Sidebar.scss';
|
import '../../assets/scss/Sidebar.scss';
|
||||||
|
|
||||||
export default class Sidebar extends React.Component{
|
export default class Sidebar extends React.Component {
|
||||||
|
|
||||||
constructor(){
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
@@ -19,7 +19,7 @@ export default class Sidebar extends React.Component{
|
|||||||
this.onToggle = this.onToggle.bind(this);
|
this.onToggle = this.onToggle.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
onToggle(){
|
onToggle() {
|
||||||
let temp = this.state.toggler;
|
let temp = this.state.toggler;
|
||||||
temp = temp === "open" ? "" : "open";
|
temp = temp === "open" ? "" : "open";
|
||||||
|
|
||||||
@@ -28,8 +28,8 @@ export default class Sidebar extends React.Component{
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
render(){
|
render() {
|
||||||
return(
|
return (
|
||||||
<div class={"Sidebar " + this.state.toggler}>
|
<div class={"Sidebar " + this.state.toggler}>
|
||||||
<a onClick={this.onToggle} class="toggler">
|
<a onClick={this.onToggle} class="toggler">
|
||||||
<i
|
<i
|
||||||
@@ -63,7 +63,7 @@ export default class Sidebar extends React.Component{
|
|||||||
|
|
||||||
<h2>Sensors</h2>
|
<h2>Sensors</h2>
|
||||||
<hr/>
|
<hr/>
|
||||||
<SensorList/>
|
{this.props.sensor.fetchedList ? <SensorList list={this.props.sensor.list}/> : null}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
14
client/js/components/sensors/SensorInfo.js
Normal file
14
client/js/components/sensors/SensorInfo.js
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
export default class SensorInfo extends React.Component{
|
||||||
|
|
||||||
|
componentDidMount(){
|
||||||
|
this.props.sensorActions.fetchSensorInfoYear('Grand Meadow', '2016');
|
||||||
|
this.props.sensorActions.fetchSensorInfoMonth('Grand Meadow', '2016', 'May');
|
||||||
|
}
|
||||||
|
render(){
|
||||||
|
return(
|
||||||
|
<div class="Content">Test123</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,41 +3,6 @@ import 'whatwg-fetch';
|
|||||||
|
|
||||||
export default class SensorList extends React.Component {
|
export default class SensorList extends React.Component {
|
||||||
|
|
||||||
constructor(){
|
|
||||||
super();
|
|
||||||
|
|
||||||
this.state = {
|
|
||||||
sensors : {},
|
|
||||||
fetching: false,
|
|
||||||
fetched: false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount(){
|
|
||||||
this.loadSensorData();
|
|
||||||
}
|
|
||||||
|
|
||||||
loadSensorData(){
|
|
||||||
this.setState({
|
|
||||||
fetching: true
|
|
||||||
});
|
|
||||||
|
|
||||||
fetch('/api/allsensors')
|
|
||||||
.then((response) => {
|
|
||||||
return response.json()
|
|
||||||
})
|
|
||||||
.then((json) => {
|
|
||||||
this.setState({
|
|
||||||
sensors: json,
|
|
||||||
fetching: false,
|
|
||||||
fetched: true
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch((e) => {
|
|
||||||
console.log('Loading sensors failed', e)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
insertSensorData = (sensor, index) => {
|
insertSensorData = (sensor, index) => {
|
||||||
const date = new Date(sensor.updated);
|
const date = new Date(sensor.updated);
|
||||||
|
|
||||||
@@ -49,11 +14,12 @@ export default class SensorList extends React.Component {
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
render(){
|
render() {
|
||||||
console.log(this.state);
|
const list = this.props.list;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{this.state.fetched ? this.state.sensors.map(this.insertSensorData) : null}
|
{list.map(this.insertSensorData)}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
14
client/js/components/utils/Loading.js
Normal file
14
client/js/components/utils/Loading.js
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
//loading icon
|
||||||
|
import loading from '../../../assets/images/loading.svg';
|
||||||
|
|
||||||
|
export default class Loading extends React.Component{
|
||||||
|
render(){
|
||||||
|
return(
|
||||||
|
<div class="Loading">
|
||||||
|
<img src={loading} alt="loading..."/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,10 +2,8 @@ import React from 'react';
|
|||||||
|
|
||||||
//components
|
//components
|
||||||
import Header from '../components/Header';
|
import Header from '../components/Header';
|
||||||
import Preview from '../components/Preview';
|
|
||||||
import Footer from '../components/Footer';
|
import Footer from '../components/Footer';
|
||||||
import Sidebar from '../components/Sidebar';
|
import Sidebar from '../components/Sidebar';
|
||||||
import Post from '../components/Post';
|
|
||||||
|
|
||||||
//css
|
//css
|
||||||
import '../../assets/css/normalize.css';
|
import '../../assets/css/normalize.css';
|
||||||
@@ -13,47 +11,21 @@ import '../../assets/scss/main.scss';
|
|||||||
import 'font-awesome/css/font-awesome.min.css';
|
import 'font-awesome/css/font-awesome.min.css';
|
||||||
import '../../assets/css/dracula.css';
|
import '../../assets/css/dracula.css';
|
||||||
|
|
||||||
//loading icon
|
|
||||||
import loading from '../../assets/images/loading.svg';
|
|
||||||
|
|
||||||
export default class Index extends React.Component {
|
export default class Index extends React.Component {
|
||||||
componentDidMount() {
|
|
||||||
this.props.actions.fetchPreview();
|
componentDidMount(){
|
||||||
this.page = this.props.params.page;
|
this.props.sensorActions.fetchSensorList();
|
||||||
this.page === 'post' ? this.props.actions.fetchPost(this.props.params.category, this.props.params.post) : "";
|
}
|
||||||
}
|
render() {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
componentWillReceiveProps(nextProps){
|
<Header/>
|
||||||
if(this.props.params !== nextProps.params){
|
|
||||||
const params = nextProps.params;
|
|
||||||
this.page = params.page;
|
|
||||||
|
|
||||||
if(typeof params.post !== 'undefined' && typeof params.category !== 'undefined'){
|
|
||||||
this.props.actions.fetchPost(params.category, params.post);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const fetched = this.props.redux.fetched;
|
|
||||||
const fetching = this.props.redux.fetching;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<Header />
|
|
||||||
<div class="Main">
|
<div class="Main">
|
||||||
{typeof this.page === 'undefined' && !fetching ? <Preview posts={this.props.redux.preview.posts} /> : null}
|
{React.cloneElement(this.props.children, this.props)}
|
||||||
{this.page === 'post' && !fetching ? <Post content={this.props.redux.post}/> : null}
|
<Sidebar sensor={Object.assign({}, this.props.sensor)}/>
|
||||||
{fetching ? loadingElement : null}
|
|
||||||
<Sidebar />
|
|
||||||
</div>
|
</div>
|
||||||
<Footer />
|
<Footer/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const loadingElement = <div class="Loading">
|
|
||||||
<img src={loading} alt="loading..."/>
|
|
||||||
</div>;
|
|
||||||
@@ -1,7 +1,13 @@
|
|||||||
import * as types from "./constants";
|
import * as types from "../constants/app";
|
||||||
import marked from 'marked';
|
import marked from 'marked';
|
||||||
import 'whatwg-fetch';
|
import 'whatwg-fetch';
|
||||||
|
|
||||||
|
export function increasePostLimit() {
|
||||||
|
return {
|
||||||
|
type: types.INCREASE_POST_LIMIT
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function initPreview(posts) {
|
function initPreview(posts) {
|
||||||
return {
|
return {
|
||||||
type: types.INIT_PREVIEW,
|
type: types.INIT_PREVIEW,
|
||||||
@@ -9,15 +15,15 @@ function initPreview(posts) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function loadPost(post){
|
function loadPost(post) {
|
||||||
return {
|
return {
|
||||||
type: types.LOAD_POST,
|
type: types.LOAD_POST,
|
||||||
post
|
post
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function fetching(){
|
function fetching() {
|
||||||
return{
|
return {
|
||||||
type: types.FETCHING
|
type: types.FETCHING
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
83
client/js/redux/actions/sensor.js
Normal file
83
client/js/redux/actions/sensor.js
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
import * as types from "../constants/sensor";
|
||||||
|
import 'whatwg-fetch';
|
||||||
|
|
||||||
|
function loadSensorList(sensor_list){
|
||||||
|
return {
|
||||||
|
type: types.LOAD_SENSOR_LIST,
|
||||||
|
sensor_list
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadSensorInfoYear(sensor_info){
|
||||||
|
return{
|
||||||
|
type: types.LOAD_SENSOR_INFO_YEAR,
|
||||||
|
sensor_info
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadSensorInfoMonth(sensor_info){
|
||||||
|
return{
|
||||||
|
type: types.LOAD_SENSOR_INFO_MONTH,
|
||||||
|
sensor_info
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function fetchingList(){
|
||||||
|
return {
|
||||||
|
type: types.FETCHING_LIST
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function fetchingInfoYear(){
|
||||||
|
return {
|
||||||
|
type: types.FETCHING_INFO_YEAR
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function fetchingInfoMonth(){
|
||||||
|
return {
|
||||||
|
type: types.FETCHING_INFO_MONTH
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function fetchSensorList(){
|
||||||
|
return (dispatch) => {
|
||||||
|
dispatch(fetchingList());
|
||||||
|
return fetch('/api/allsensors')
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(json => {
|
||||||
|
dispatch(loadSensorList(json));
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.log(error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function fetchSensorInfoYear(location, year){
|
||||||
|
return (dispatch) => {
|
||||||
|
dispatch(fetchingInfoYear());
|
||||||
|
return fetch(`/api/sensor/${location}/${year}`)
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(json => {
|
||||||
|
dispatch(loadSensorInfoYear(json));
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.log(error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function fetchSensorInfoMonth(location, year, month){
|
||||||
|
return (dispatch) => {
|
||||||
|
dispatch(fetchingInfoMonth());
|
||||||
|
return fetch(`/api/sensor/${location}/${year}/${month}`)
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(json => {
|
||||||
|
dispatch(loadSensorInfoMonth(json));
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.log(error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
//constants
|
|
||||||
export const INIT_PREVIEW = 'INIT_PREVIEW';
|
|
||||||
export const FILTER_PREVIEW = 'FILTER_PREVIEW';
|
|
||||||
export const LOAD_POST = 'LOAD_POST';
|
|
||||||
export const FETCHING = 'FETCHING';
|
|
||||||
6
client/js/redux/constants/app.js
Normal file
6
client/js/redux/constants/app.js
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
//constants
|
||||||
|
export const INCREASE_POST_LIMIT = 'INCREASE_POST_LIMIT';
|
||||||
|
export const INIT_PREVIEW = 'INIT_PREVIEW';
|
||||||
|
export const FETCHING = 'FETCHING';
|
||||||
|
export const FILTER_PREVIEW = 'FILTER_PREVIEW';
|
||||||
|
export const LOAD_POST = 'LOAD_POST';
|
||||||
10
client/js/redux/constants/sensor.js
Normal file
10
client/js/redux/constants/sensor.js
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
//constants
|
||||||
|
export const LOAD_SENSOR_LIST = 'LOAD_SENSOR_LIST';
|
||||||
|
export const LOAD_SENSOR_INFO_YEAR = 'LOAD_SENSOR_INFO_YEAR';
|
||||||
|
export const LOAD_SENSOR_INFO_MONTH = 'LOAD_SENSOR_INFO_MONTH';
|
||||||
|
|
||||||
|
|
||||||
|
//fetching
|
||||||
|
export const FETCHING_LIST = 'FETCHING_LIST';
|
||||||
|
export const FETCHING_INFO_YEAR = 'FETCHING_INFO_YEAR';
|
||||||
|
export const FETCHING_INFO_MONTH = 'FETCHING_INFO_MONTH';
|
||||||
@@ -1,29 +1,19 @@
|
|||||||
//just using one reducer - use combineReducers from redux to modularize things
|
|
||||||
import {
|
|
||||||
combineReducers
|
|
||||||
} from 'redux';
|
|
||||||
import {
|
|
||||||
routerReducer
|
|
||||||
} from 'react-router-redux';
|
|
||||||
|
|
||||||
//import typs
|
//import typs
|
||||||
import * as types from './constants';
|
import * as types from '../constants/app';
|
||||||
|
|
||||||
//defaults -
|
//defaults -
|
||||||
const defaultState = {
|
const defaultState = {
|
||||||
preview: {
|
preview: {
|
||||||
posts: []
|
posts: []
|
||||||
},
|
},
|
||||||
filteredPreview: {
|
|
||||||
posts: []
|
|
||||||
},
|
|
||||||
post: "",
|
post: "",
|
||||||
fetched: false,
|
fetched: false,
|
||||||
fetching: false
|
fetching: false,
|
||||||
|
postLimit: 10
|
||||||
};
|
};
|
||||||
|
|
||||||
//default reducer
|
//default reducer
|
||||||
function reducer(state = defaultState, action) {
|
export default function app(state = defaultState, action) {
|
||||||
//every reducer gets called when an action is called - we check for the type to modify our state accordingly
|
//every reducer gets called when an action is called - we check for the type to modify our state accordingly
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case types.INIT_PREVIEW:
|
case types.INIT_PREVIEW:
|
||||||
@@ -32,10 +22,6 @@ function reducer(state = defaultState, action) {
|
|||||||
fetched: true,
|
fetched: true,
|
||||||
fetching: false
|
fetching: false
|
||||||
});
|
});
|
||||||
case types.FILTER_PREVIEW:
|
|
||||||
return Object.assign({}, state, {
|
|
||||||
filteredPreview: Object.assign({}, state.filteredPreview, action.posts)
|
|
||||||
});
|
|
||||||
case types.LOAD_POST:
|
case types.LOAD_POST:
|
||||||
return Object.assign({}, state, {
|
return Object.assign({}, state, {
|
||||||
post: action.post,
|
post: action.post,
|
||||||
@@ -44,18 +30,15 @@ function reducer(state = defaultState, action) {
|
|||||||
});
|
});
|
||||||
case types.FETCHING:
|
case types.FETCHING:
|
||||||
return Object.assign({}, state, {
|
return Object.assign({}, state, {
|
||||||
fetched : false,
|
fetched: false,
|
||||||
fetching: true
|
fetching: true
|
||||||
});
|
});
|
||||||
|
case types.INCREASE_POST_LIMIT:
|
||||||
|
return Object.assign({}, state, {
|
||||||
|
postLimit: state.postLimit + 10
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
//return present state if no actions get called
|
//return present state if no actions get called
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
const allReducers = combineReducers({
|
|
||||||
reducer,
|
|
||||||
routing: routerReducer
|
|
||||||
});
|
|
||||||
|
|
||||||
export default allReducers;
|
|
||||||
14
client/js/redux/reducers/reducers.js
Normal file
14
client/js/redux/reducers/reducers.js
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
//just using one reducer - use combineReducers from redux to modularize things
|
||||||
|
import {combineReducers} from 'redux';
|
||||||
|
import {routerReducer} from 'react-router-redux';
|
||||||
|
|
||||||
|
import app from './app';
|
||||||
|
import sensor from './sensor';
|
||||||
|
|
||||||
|
const allReducers = combineReducers({
|
||||||
|
app,
|
||||||
|
sensor,
|
||||||
|
routing: routerReducer
|
||||||
|
});
|
||||||
|
|
||||||
|
export default allReducers;
|
||||||
59
client/js/redux/reducers/sensor.js
Normal file
59
client/js/redux/reducers/sensor.js
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
//import typs
|
||||||
|
import * as types from '../constants/sensor';
|
||||||
|
|
||||||
|
//defaults -
|
||||||
|
const defaultState = {
|
||||||
|
list : [],
|
||||||
|
infoMonth: [],
|
||||||
|
infoYear: [],
|
||||||
|
|
||||||
|
fetchingList: false,
|
||||||
|
fetchingInfoMonth: false,
|
||||||
|
fetchingInfoYear: false,
|
||||||
|
|
||||||
|
fetchedList: false,
|
||||||
|
fetchedInfoMonth: false,
|
||||||
|
fetchedInfoYear: false
|
||||||
|
};
|
||||||
|
|
||||||
|
//default reducer
|
||||||
|
export default function app(state = defaultState, action) {
|
||||||
|
switch(action.type){
|
||||||
|
case types.FETCHING_LIST:
|
||||||
|
return Object.assign({}, state, {
|
||||||
|
fetchingList: true,
|
||||||
|
fetchedList: false
|
||||||
|
});
|
||||||
|
case types.FETCHING_INFO_MONTH:
|
||||||
|
return Object.assign({}, state, {
|
||||||
|
fetchingInfoMonth: true,
|
||||||
|
fetchedInfoMonth: false
|
||||||
|
});
|
||||||
|
case types.FETCHING_INFO_YEAR:
|
||||||
|
return Object.assign({}, state, {
|
||||||
|
fetchingInfoYear: true,
|
||||||
|
fetchedInfoYear: false
|
||||||
|
});
|
||||||
|
|
||||||
|
case types.LOAD_SENSOR_LIST:
|
||||||
|
return Object.assign({}, state, {
|
||||||
|
list: action.sensor_list,
|
||||||
|
fetchingList: false,
|
||||||
|
fetchedList: true
|
||||||
|
});
|
||||||
|
case types.LOAD_SENSOR_INFO_MONTH:
|
||||||
|
return Object.assign({}, state, {
|
||||||
|
infoMonth: action.sensor_info,
|
||||||
|
fetchingInfoMonth: false,
|
||||||
|
fetchedInfoMonth: true
|
||||||
|
});
|
||||||
|
case types.LOAD_SENSOR_INFO_YEAR:
|
||||||
|
return Object.assign({}, state, {
|
||||||
|
infoYear: action.sensor_info,
|
||||||
|
fetchingInfoYear: false,
|
||||||
|
fetchedInfoYear: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
//return present state if no actions get called
|
||||||
|
return state;
|
||||||
|
}
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
import {applyMiddleware, createStore} from 'redux';
|
|
||||||
import {syncHistoryWithStore} from 'react-router-redux';
|
|
||||||
import {browserHistory} from 'react-router';
|
|
||||||
import thunk from 'redux-thunk';
|
|
||||||
import logger from 'redux-logger';
|
import logger from 'redux-logger';
|
||||||
|
import thunk from 'redux-thunk';
|
||||||
|
import {applyMiddleware, createStore} from 'redux';
|
||||||
|
import {browserHistory} from 'react-router';
|
||||||
|
import {syncHistoryWithStore} from 'react-router-redux';
|
||||||
|
|
||||||
import reducers from './reducers';
|
import reducers from './reducers/reducers';
|
||||||
|
|
||||||
const middleware = applyMiddleware(thunk, logger());
|
const middleware = applyMiddleware(thunk, logger());
|
||||||
|
|
||||||
|
|||||||
@@ -40,8 +40,8 @@ function parse_dir(dir, folder_name){
|
|||||||
filename: post.slice(0, post.length - 3),
|
filename: post.slice(0, post.length - 3),
|
||||||
category: folder_name,
|
category: folder_name,
|
||||||
date: post.slice(0, 10),
|
date: post.slice(0, 10),
|
||||||
title: tokens[0].text,
|
title: `<h1>${tokens[0].text}</h1>`,
|
||||||
intro: tokens[1].text
|
intro: marked(tokens[1].text)
|
||||||
}
|
}
|
||||||
json.posts.push(temp);
|
json.posts.push(temp);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,9 +6,12 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "webpack && babel-node metadata.js",
|
"build": "webpack && babel-node metadata.js",
|
||||||
"c9": "webpack-dev-server --port $PORT --host $IP --hot --content-base dist --history-api-fallback",
|
"c9": "webpack-dev-server --port $PORT --host $IP --hot --content-base dist --history-api-fallback",
|
||||||
|
"check_gzip_size": "gzip -9 -c ./public/client.min.js | wc -c | numfmt --to=iec-i --suffix=B --padding=10",
|
||||||
|
"deploy" : "npm run get_dependencies && npm run prod && ./mywebsite",
|
||||||
"dev": "webpack-dev-server --content-base public --inline --hot --history-api-fallback",
|
"dev": "webpack-dev-server --content-base public --inline --hot --history-api-fallback",
|
||||||
"prod": "export NODE_ENV=production && webpack -p && babel-node metadata.js",
|
"get_dependencies": "go get ./server && npm install",
|
||||||
"prod-win": "set NODE_ENV=production && webpack -p && babel-node metadata.js"
|
"prod": "export NODE_ENV=production && webpack -p && babel-node metadata.js && go build ./server/mywebsite.go",
|
||||||
|
"prod-win": "set NODE_ENV=production && webpack -p && babel-node metadata.js && go build ./server/mywebsite.go"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
|||||||
206
posts/Web Stuff/2016-08-30-ubuntu16-mongodb.md
Normal file
206
posts/Web Stuff/2016-08-30-ubuntu16-mongodb.md
Normal file
@@ -0,0 +1,206 @@
|
|||||||
|
# Installing and setting up authentication with MongoDB on Ubuntu 16.04.1
|
||||||
|
|
||||||
|
I recently spun up a new virtual machine on Digital Ocean and I decided to try the latest version
|
||||||
|
of Ubuntu.. because why not? I wanted to just host a MongoDB instance because I was going to host my site on Google's App Engine.
|
||||||
|
Turns out Google blocks any outgoing requests that are not HTTP.
|
||||||
|
(This prevents the Go [MGO](https://godoc.org/gopkg.in/mgo.v2) driver from connecting)
|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
|
## Installing MongoDB
|
||||||
|
|
||||||
|
Luckily Digital Ocean offers great documentation when it comes to installing software. They usually have guides
|
||||||
|
and tutorials that are fairly up to date. I first followed [this guide](https://www.digitalocean.com/community/tutorials/how-to-install-mongodb-on-ubuntu-16-04)
|
||||||
|
to get MongoDB installed.
|
||||||
|
|
||||||
|
On previous versions of Ubuntu, MongoDB was installed as a service. Newer versions use Systemd to run the database.
|
||||||
|
Systemd uses different commands to check database statistics.
|
||||||
|
|
||||||
|
The Systemd configuration is located in a few different places, but mine seems to work in the following directory.
|
||||||
|
Here the run command can be edited, although this probably does not need to be done
|
||||||
|
as most settings can be adjusted in the MongoDB config file.
|
||||||
|
|
||||||
|
MongoDB systemd config
|
||||||
|
```bash
|
||||||
|
/lib/systemd/system/mongod.service
|
||||||
|
```
|
||||||
|
|
||||||
|
MongoDB config
|
||||||
|
```bash
|
||||||
|
/etc/mongod.conf
|
||||||
|
```
|
||||||
|
|
||||||
|
Service as well as systemd can be used to start and stop MongoDB.
|
||||||
|
These commands will do the same thing, which is nice.
|
||||||
|
```bash
|
||||||
|
sudo service mongod <start,stop,restart>
|
||||||
|
sudo systemctl <start,stop,restart> mongod
|
||||||
|
```
|
||||||
|
|
||||||
|
Although you can start, stop, and restart MongoDB with service and systemctl,
|
||||||
|
MongoDB does not show up when listing all services.
|
||||||
|
```bash
|
||||||
|
sudo service --status-all
|
||||||
|
```
|
||||||
|
|
||||||
|
Systemclt must now be used
|
||||||
|
```bash
|
||||||
|
sudo systemctl status
|
||||||
|
```
|
||||||
|
|
||||||
|
## Enabling security for external connections
|
||||||
|
|
||||||
|
Any time a port is opened up for a MongoDB instance security precautions must be taken.
|
||||||
|
[There are thousands of MongoDB instances that are exposed to the internet.](http://www.securityweek.com/thousands-mongodb-databases-found-exposed-internet)
|
||||||
|
There are a few reasons for this. MongoDB is usually run on the same machine of web applications,
|
||||||
|
therefore the port it is running on should not be opened up at all. MongoDB can also be tricky to configure properly
|
||||||
|
and a system admin must know exactly what they are doing in making the database secure. Login credentials do not
|
||||||
|
even need to be set up if the port it is running on is not even open. As soon as that port is opened up,
|
||||||
|
unwanted guests can gain easy access, especially if it is running on the default port (27018).
|
||||||
|
|
||||||
|
The first thing to do when running MongoDB for external access is to run it on a completely different port.
|
||||||
|
Why run it on the port where attackers know exactly what they are looking for?
|
||||||
|
|
||||||
|
Security authentication must be enabled in the MongoDB config file. It is turned off by default.
|
||||||
|
Make sure to do this after creating user accounts or else access will be denied.
|
||||||
|
|
||||||
|
## Creating user accounts
|
||||||
|
|
||||||
|
Although this probably isn't the most secure thing to do, I start out by creating a root admin.
|
||||||
|
I do this because it allows me control over any database and it gives me easier access and power.
|
||||||
|
My database doesn't store any sensative information so I am okay with this.
|
||||||
|
|
||||||
|
A root admin can be created by connecting to the server with the command `mongo` using the admin database.
|
||||||
|
```bash
|
||||||
|
mongo
|
||||||
|
use admin
|
||||||
|
```
|
||||||
|
|
||||||
|
Databases can also be listed
|
||||||
|
```bash
|
||||||
|
show dbs
|
||||||
|
```
|
||||||
|
|
||||||
|
Use the admin database and create a root user
|
||||||
|
```bash
|
||||||
|
db.createUser(
|
||||||
|
{
|
||||||
|
user: "admin",
|
||||||
|
pwd: "password",
|
||||||
|
roles: ["root"]
|
||||||
|
}
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
Verify that the user was created
|
||||||
|
```bash
|
||||||
|
db.getUsers()
|
||||||
|
```
|
||||||
|
|
||||||
|
Lets go enable security
|
||||||
|
```bash
|
||||||
|
sudo nano /etc/mongod.conf
|
||||||
|
```
|
||||||
|
|
||||||
|
Uncomment `#security` by removing the `#` and add this line after
|
||||||
|
```bash
|
||||||
|
security:
|
||||||
|
authorization: enabled
|
||||||
|
```
|
||||||
|
|
||||||
|
Restart database or reboot the system
|
||||||
|
```bash
|
||||||
|
sudo systemctl restart mongod
|
||||||
|
```
|
||||||
|
|
||||||
|
We can now authenticate to the database as our newly created root admin
|
||||||
|
```bash
|
||||||
|
mongo admin --port 27017 -u 'admin' -p 'password'
|
||||||
|
```
|
||||||
|
|
||||||
|
## Difficulties configuring users
|
||||||
|
|
||||||
|
When I first started setting up users I had a heck of a time. I wasn't sure which database to use for certain
|
||||||
|
users or what permissions I had to give them. Part of this is due to my impatience of not reading the
|
||||||
|
documentation thoroughly.
|
||||||
|
|
||||||
|
Server wide users/admins MUST be in the admin database. We created a root use in the admin database
|
||||||
|
previously so that user should have access to any database in the server. All database users must be in
|
||||||
|
their respective database. For example if we want to use the "test" database, users must be created
|
||||||
|
within this database in order to gain access. Although access can be gained with a root admin account,
|
||||||
|
this is something that should not be done in production.
|
||||||
|
|
||||||
|
Lets create a read/write user for the "test" database
|
||||||
|
```bash
|
||||||
|
use test
|
||||||
|
|
||||||
|
db.createUser(
|
||||||
|
{
|
||||||
|
user: "user",
|
||||||
|
pwd: "password",
|
||||||
|
roles: ["readWrite"]
|
||||||
|
}
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
Verify that the user was created
|
||||||
|
```bash
|
||||||
|
db.getUsers()
|
||||||
|
```
|
||||||
|
|
||||||
|
Login as this user
|
||||||
|
```bash
|
||||||
|
mongo test --port 27017 -u 'user' -p 'admin'
|
||||||
|
```
|
||||||
|
|
||||||
|
Now a proper user should be set up for read/write access. This is the exact method I used to
|
||||||
|
gain access to an external database the Go's MGO MongoDB database driver.
|
||||||
|
|
||||||
|
### Scripts for easier access to the database
|
||||||
|
|
||||||
|
I created some shell scripts on my database server with credentials saved because I use long random passwords
|
||||||
|
that I cannot remember off the top of my head. (keep in mind this increases security risks)
|
||||||
|
It took me awhile to get this to work because some syntax did not work for me when inside of a shell script.
|
||||||
|
|
||||||
|
I created a file called `mongo_admin.sh` and one called `mongo_user.sh`.
|
||||||
|
|
||||||
|
The files look like this
|
||||||
|
```bash
|
||||||
|
mongo admin --port 27017 -u 'user' -p 'password'
|
||||||
|
```
|
||||||
|
|
||||||
|
This was frustrating because I figured out it would not work when using double quotes
|
||||||
|
inside of the shell script, although double quotes work when issuing the command manually.
|
||||||
|
I also realized that a database must be specified in order to connect. This can be done
|
||||||
|
like above, or like this.
|
||||||
|
```bash
|
||||||
|
mongo --port 27017 -u 'user' -p 'password' --authenticationDatabase admin
|
||||||
|
```
|
||||||
|
|
||||||
|
## Firewall
|
||||||
|
|
||||||
|
Firewall rules must be changed to enable external database access. First of all, edit the MongoDB config
|
||||||
|
file and change the default database port. Once that is changed use [UFW](https://help.ubuntu.com/community/UFW)
|
||||||
|
to change firewall rules. For this example I will change MongoDB to run on port 27018.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ufw enable
|
||||||
|
ufw status
|
||||||
|
ufw allow 27018/tcp
|
||||||
|
```
|
||||||
|
|
||||||
|
This will allow all incoming connections to the database. It is advised to only allow
|
||||||
|
incoming connections from the web application server that is being used.
|
||||||
|
|
||||||
|
Allow to certain ip
|
||||||
|
```bash
|
||||||
|
ufw allow from <ip> to any port 27018
|
||||||
|
```
|
||||||
|
|
||||||
|
## Conclusion
|
||||||
|
|
||||||
|
MongoDB can be tricky to set up, but it is highly recommened to go through the entire process.
|
||||||
|
In the end I decided not to host my MongoDB instance externally so I did not go through
|
||||||
|
the entire [MongoDB security check list](https://docs.mongodb.com/v2.6/administration/security-checklist/#audit-system-activity).
|
||||||
|
There are a few other things to do such as adding encryption and monitoring system activity, but
|
||||||
|
I covered most of the important issues.
|
||||||
13
readme.md
13
readme.md
@@ -4,20 +4,17 @@
|
|||||||
- Go App Engine
|
- Go App Engine
|
||||||
|
|
||||||
`npm run build` - builds the application using webpack and runs metadata.js
|
`npm run build` - builds the application using webpack and runs metadata.js
|
||||||
|
`npm run
|
||||||
- metadata.js recursivly scans the posts folder for markdown files and then parses each into into a json object
|
- metadata.js recursivly scans the posts folder for markdown files and then parses each into into a json object
|
||||||
- the posts folder is then copied into the dist folder
|
- the posts folder is then copied into the dist folder
|
||||||
|
|
||||||
## TODO
|
## TODO
|
||||||
- fix font sizing
|
- sticky footer
|
||||||
- clean up css
|
- clean up css
|
||||||
- adjust animations
|
- adjust animations
|
||||||
|
|
||||||
- fix goapp serve and webpack-dev-server so paths work correctly
|
- fix go and webpack-dev-server so paths work correctly
|
||||||
- posts page
|
|
||||||
- add sensor page
|
- add sensor page
|
||||||
- finally do writeups on my projects
|
|
||||||
- clean go code up
|
|
||||||
- host on app engine (run mongodb on compute engine???)
|
|
||||||
- pull everything off digital ocean
|
|
||||||
- add paging
|
- add paging
|
||||||
|
|
||||||
|
- finally do writeups on my projects
|
||||||
@@ -11,7 +11,7 @@ func DiscordRedirect(w http.ResponseWriter, r *http.Request, _ httprouter.Params
|
|||||||
http.Redirect(w, r, "https://discordapp.com/invite/0Z2tzxKECEj2BHwj", 301)
|
http.Redirect(w, r, "https://discordapp.com/invite/0Z2tzxKECEj2BHwj", 301)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Redirect to discord
|
// Redirect to vpn
|
||||||
func VPNRedirect(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
func VPNRedirect(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||||
http.Redirect(w, r, "https://mitchel.io:943", 301)
|
http.Redirect(w, r, "https://mitchel.io:943", 301)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ func (s *Data) toJson() string {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//default store structure
|
||||||
func (s *Data) StoreData() error {
|
func (s *Data) StoreData() error {
|
||||||
|
|
||||||
if db.Mongo.Connected() {
|
if db.Mongo.Connected() {
|
||||||
@@ -57,13 +58,13 @@ func (s *Data) StoreData() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//handle queries for all sensors page
|
//handle queries for all sensors page
|
||||||
//********************************************************************************
|
|
||||||
type DataStore_AllSensors struct {
|
type DataStore_AllSensors struct {
|
||||||
ID string `json:"location" bson:"_id"`
|
ID string `json:"location" bson:"_id"`
|
||||||
Temperature float64 `json:"temperature" bson:"temperature"`
|
Temperature float64 `json:"temperature" bson:"temperature"`
|
||||||
Updated time.Time `json:"updated" bson:"updated"`
|
Updated time.Time `json:"updated" bson:"updated"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//get latest update from each unique sensor
|
||||||
func GetAllSensors() ([]DataStore_AllSensors, error) {
|
func GetAllSensors() ([]DataStore_AllSensors, error) {
|
||||||
|
|
||||||
s := []DataStore_AllSensors{}
|
s := []DataStore_AllSensors{}
|
||||||
@@ -88,52 +89,4 @@ func GetAllSensors() ([]DataStore_AllSensors, error) {
|
|||||||
} else {
|
} else {
|
||||||
return s, errors.New("Query failed")
|
return s, errors.New("Query failed")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//********************************************************************************
|
|
||||||
|
|
||||||
//get sensor information by location
|
|
||||||
//********************************************************************************
|
|
||||||
type DataStore_SensorByLocation struct {
|
|
||||||
Id sensorByLocation `json:"_id" bson:"_id"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type sensorByLocation struct {
|
|
||||||
Year int `json:"year" bson:"year"`
|
|
||||||
Month int `json:"month" bson:"month"`
|
|
||||||
Location string `json:"location" bson:"location"`
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
func GetSensorInfoByLocation(sensor_location string) ([]DataStore_SensorByLocation, error) {
|
|
||||||
s := []DataStore_SensorByLocation{}
|
|
||||||
if db.Mongo.Connected() == true {
|
|
||||||
session := db.Mongo.Session.Copy()
|
|
||||||
defer session.Close()
|
|
||||||
c := session.DB(db.Mongo.Info.Database).C(collection)
|
|
||||||
err := c.Pipe([]bson.M{{"$project": bson.M{"location": "$location", "year": bson.M{"$year": "$updated"}, "month": bson.M{"$month": "$updated"}}},
|
|
||||||
bson.M{"$match": bson.M{"location": sensor_location}},
|
|
||||||
bson.M{"$group": bson.M{"_id": bson.M{"year": "$year", "month": "$month", "location": "$location"}}},
|
|
||||||
bson.M{"$sort": bson.M{"_id.year": -1, "_id.month": -1}}}).All(&s)
|
|
||||||
if err != nil {
|
|
||||||
log.Println(err)
|
|
||||||
return s, nil
|
|
||||||
}
|
|
||||||
return s, nil
|
|
||||||
} else {
|
|
||||||
return s, errors.New("Query failed")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
//********************************************************************************
|
|
||||||
|
|
||||||
/*************************
|
|
||||||
testStore := model.SensorData{
|
|
||||||
ID: bson.NewObjectId(),
|
|
||||||
Temperature: 34.2,
|
|
||||||
Humidity: 33.22,
|
|
||||||
Location: "Grand Meadow",
|
|
||||||
Updated: time.Now(),
|
|
||||||
}
|
|
||||||
**************************/
|
|
||||||
@@ -4,29 +4,14 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"github.com/NYTimes/gziphandler"
|
||||||
//local import paths relative to app.yaml file
|
|
||||||
"mywebsite/server/controller/api"
|
"mywebsite/server/controller/api"
|
||||||
"mywebsite/server/db"
|
"mywebsite/server/db"
|
||||||
"mywebsite/server/route"
|
"mywebsite/server/route"
|
||||||
"mywebsite/server/utils"
|
"mywebsite/server/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
/* for app engine
|
|
||||||
func init() {
|
|
||||||
configurations := utils.ReadConfig()
|
|
||||||
|
|
||||||
db.Configure(configurations.Database)
|
|
||||||
api.Configure(configurations.Api)
|
|
||||||
|
|
||||||
db.Mongo.Connect()
|
|
||||||
|
|
||||||
router := route.Routes()
|
|
||||||
|
|
||||||
http.Handle("/", router)
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
func main(){
|
func main(){
|
||||||
configurations := utils.ReadConfig()
|
configurations := utils.ReadConfig()
|
||||||
|
|
||||||
@@ -34,7 +19,10 @@ func main(){
|
|||||||
api.Configure(configurations.Api)
|
api.Configure(configurations.Api)
|
||||||
|
|
||||||
db.Mongo.Connect()
|
db.Mongo.Connect()
|
||||||
|
|
||||||
|
//register middleware
|
||||||
|
handle := gziphandler.GzipHandler(route.Routes())
|
||||||
|
|
||||||
log.Println("Starting Server...")
|
log.Println("Starting Server...")
|
||||||
log.Println(http.ListenAndServe(":"+strconv.Itoa(configurations.Port), route.Routes()))
|
log.Println(http.ListenAndServe(":"+strconv.Itoa(configurations.Port), handle))
|
||||||
}
|
}
|
||||||
@@ -14,7 +14,7 @@ import (
|
|||||||
type Config struct {
|
type Config struct {
|
||||||
Database db.DatabaseInfo `json:"database"`
|
Database db.DatabaseInfo `json:"database"`
|
||||||
Api api.ApiInfo `json:"api"`
|
Api api.ApiInfo `json:"api"`
|
||||||
Port int `json:"port"`
|
Port int `json:"port"`
|
||||||
}
|
}
|
||||||
|
|
||||||
//read the config file and return JsonObject struct
|
//read the config file and return JsonObject struct
|
||||||
@@ -31,8 +31,6 @@ func ReadConfig() Config {
|
|||||||
|
|
||||||
log.Printf("%s\n", string(file))
|
log.Printf("%s\n", string(file))
|
||||||
|
|
||||||
//m := new(Dispatch)
|
|
||||||
//var m interface{}
|
|
||||||
var result Config
|
var result Config
|
||||||
|
|
||||||
err := json.Unmarshal(file, &result)
|
err := json.Unmarshal(file, &result)
|
||||||
|
|||||||
Reference in New Issue
Block a user