1
0
mirror of https://github.com/mgerb/mywebsite synced 2026-01-10 18:02:51 +00:00

Merge pull request #2 from mgerb/react

React
This commit is contained in:
2016-09-05 11:30:12 -07:00
committed by GitHub
31 changed files with 600 additions and 278 deletions

View File

@@ -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
export function bubble() {

View File

@@ -1,19 +1,19 @@
.Content {
flex: 1;
flex-wrap: wrap;
.post + .post {
flex: 1;
flex-wrap: wrap;
min-width: 0;
.post+ .post {
margin-top: 2em;
}
.date {
display: block;
margin: 0;
opacity: 0.7;
@media (min-width: 1200px){
position: absolute;
width: 7em;
text-align: right;
margin-left: -8em;
@media (min-width: 1200px) {
position: absolute;
width: 7em;
text-align: right;
margin-left: -8em;
}
}
.intro {
@@ -28,4 +28,7 @@
pre {
white-space: pre-wrap;
}
.btn {
width: 100%;
}
}

View File

@@ -33,7 +33,7 @@ $transitionDuration: 0.4s;
&:before {
content: " ";
display: block;
background: rgba(0,0,0,0.4);
background: rgba(0, 0, 0, 0.4);
position: absolute;
height: 100%;
width: 100vw;

View File

@@ -1,20 +1,23 @@
@import './utils.scss';
$linkColor: #3598db;
$backgroundColor: #FFFFFF;
html {
font-family: 'Roboto Slab', serif;
max-width: 100%;
overflow-x: hidden;
overflow-y: scroll;
overflow: auto;
@media (max-width: 768px) {
overflow-x: hidden;
}
}
body {
max-width: 100%;
overflow-x: hidden;
overflow: hidden;
background-color: $backgroundColor;
position: relative;
-webkit-font-smoothing: subpixel-antialiased;
-webkit-font-smoothing: antialiased;
font-weight: 300;
}
h1,
h2,
h3,
@@ -25,9 +28,11 @@ h6 {
font-weight: 400;
line-height: 1em;
}
p {
font-weight: 300;
}
*,
*:after,
*:before {
@@ -35,6 +40,7 @@ p {
-moz-box-sizing: border-box;
box-sizing: border-box;
}
a {
background-color: transparent;
color: $linkColor;
@@ -43,11 +49,13 @@ a {
color: $linkColor;
}
}
hr {
margin: 1em;
border: 0;
border-top: 1px solid #eee;
}
.Footer,
.Main {
display: flex;
@@ -59,9 +67,11 @@ hr {
width: 1em;
}
}
.Main {
padding-top: 1em;
}
.Header {
width: 100%;
background: url("../images/header.jpg");
@@ -74,8 +84,20 @@ hr {
text-align: center;
}
}
.Loading{
.Loading {
display: flex;
flex: 1;
justify-content: center;
}
.btn {
background-color: $backgroundColor;
border: 1px solid #DADADA;
border-radius: .2em;
cursor: pointer;
padding: .5em;
&:hover {
background-color: #F3F3F3;
}
}

View File

@@ -9,39 +9,38 @@ import {connect, Provider} from 'react-redux';
import store, {history} from './redux/store';
//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 Preview from './components/Preview';
import Post from './components/Post';
import SensorInfo from './components/sensors/SensorInfo';
class Main extends React.Component{
render(){
return(
<div>{React.cloneElement(this.props.children, this.props)}</div>
);
}
}
function mapStateToProps(state){
function mapStateToProps(state) {
return {
redux: state.reducer
app: state.app,
sensor: state.sensor
}
}
function mapDispatchToProps(dispatch){
return{
actions: bindActionCreators(actions, dispatch)
function mapDispatchToProps(dispatch) {
return {
appActions: bindActionCreators(appActions, dispatch),
sensorActions: bindActionCreators(sensorActions, dispatch)
}
}
const App = connect(mapStateToProps, mapDispatchToProps)(Main);
const App = connect(mapStateToProps, mapDispatchToProps)(Index);
ReactDOM.render((
<Provider store={store}>
<Router history={history}>
<Route path="/" component={App}>
<IndexRoute component={Index}/>
<Route path="/:page(/:category)(/:post)" component={Index}/>
<IndexRoute component={Preview}/>
<Route path="post/:category/:post" component={Post}/>
<Route path="sensor/:location/:year/:month" component={SensorInfo}/>
</Route>
</Router>
</Provider>
),document.getElementById('app'));
), document.getElementById('app'));

View File

@@ -2,10 +2,10 @@ import React from 'react';
import '../../assets/scss/Footer.scss';
export default class Footer extends React.Component{
export default class Footer extends React.Component {
render(){
return(
render() {
return (
<div class="Footer">
Site created and maintained by Mitchell Gerber
</div>

View File

@@ -1,13 +1,13 @@
import React from 'react';
import {bubble} from '../../assets/js/bubble';
export default class Header extends React.Component{
componentDidMount(){
export default class Header extends React.Component {
componentDidMount() {
bubble();
}
render(){
return(
render() {
return (
<header id="header" class="Header">
<canvas id="canvas" width="854" height="709"></canvas>
</header>

View File

@@ -1,28 +1,43 @@
import hljs from 'highlight.js';
import marked from 'marked';
import React from 'react';
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';
const renderer = new marked.Renderer();
marked.setOptions({
langPrefix: 'hljs ',
highlight: (code) => {
return hljs.highlightAuto(code).value;
}
langPrefix: 'hljs ',
highlight: (code) => {
return hljs.highlightAuto(code).value;
}
});
export default class Post extends React.Component{
export default class Post extends React.Component {
render(){
return(
componentDidMount() {
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 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>
<Link to="/" class="link"><i class="fa fa-caret-left" aria-hidden="true"></i> Home</Link>
</div>
);
}
}

View File

@@ -1,20 +1,26 @@
import React from 'react';
import {Link} from 'react-router';
//components
import Loading from './utils/Loading';
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 = [];
for (let i in posts){
for (let i = 0; i < this.props.app.postLimit && i < posts.length; i++) {
elements.push(
<div class="post" key={i}>
<div class="date">
{posts[i].date}
</div>
<h1 class="intro" >{posts[i].title.toString()}</h1>
<p>{posts[i].intro.toString()}</p>
<div dangerouslySetInnerHTML={{__html : posts[i].title.toString() + posts[i].intro.toString()}} />
<p>
<Link class="link" to={`/post/${posts[i].category}/${posts[i].filename}`}>
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;
}
render(){
const posts = this.props.posts;
render() {
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 (
<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>
);
}

View File

@@ -7,9 +7,9 @@ import SensorList from './sensors/SensorList';
import me from '../../assets/images/me.jpg';
import '../../assets/scss/Sidebar.scss';
export default class Sidebar extends React.Component{
export default class Sidebar extends React.Component {
constructor(){
constructor() {
super();
this.state = {
@@ -19,7 +19,7 @@ export default class Sidebar extends React.Component{
this.onToggle = this.onToggle.bind(this);
}
onToggle(){
onToggle() {
let temp = this.state.toggler;
temp = temp === "open" ? "" : "open";
@@ -28,8 +28,8 @@ export default class Sidebar extends React.Component{
});
}
render(){
return(
render() {
return (
<div class={"Sidebar " + this.state.toggler}>
<a onClick={this.onToggle} class="toggler">
<i
@@ -63,7 +63,7 @@ export default class Sidebar extends React.Component{
<h2>Sensors</h2>
<hr/>
<SensorList/>
{this.props.sensor.fetchedList ? <SensorList list={this.props.sensor.list}/> : null}
</div>
);
}

View 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>
);
}
}

View File

@@ -3,41 +3,6 @@ import 'whatwg-fetch';
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) => {
const date = new Date(sensor.updated);
@@ -49,11 +14,12 @@ export default class SensorList extends React.Component {
</div>
);
}
render(){
console.log(this.state);
render() {
const list = this.props.list;
return (
<div>
{this.state.fetched ? this.state.sensors.map(this.insertSensorData) : null}
{list.map(this.insertSensorData)}
</div>
)
}

View 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>
);
}
}

View File

@@ -2,10 +2,8 @@ import React from 'react';
//components
import Header from '../components/Header';
import Preview from '../components/Preview';
import Footer from '../components/Footer';
import Sidebar from '../components/Sidebar';
import Post from '../components/Post';
//css
import '../../assets/css/normalize.css';
@@ -13,47 +11,21 @@ import '../../assets/scss/main.scss';
import 'font-awesome/css/font-awesome.min.css';
import '../../assets/css/dracula.css';
//loading icon
import loading from '../../assets/images/loading.svg';
export default class Index extends React.Component {
componentDidMount() {
this.props.actions.fetchPreview();
this.page = this.props.params.page;
this.page === 'post' ? this.props.actions.fetchPost(this.props.params.category, this.props.params.post) : "";
}
componentWillReceiveProps(nextProps){
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 />
componentDidMount(){
this.props.sensorActions.fetchSensorList();
}
render() {
return (
<div>
<Header/>
<div class="Main">
{typeof this.page === 'undefined' && !fetching ? <Preview posts={this.props.redux.preview.posts} /> : null}
{this.page === 'post' && !fetching ? <Post content={this.props.redux.post}/> : null}
{fetching ? loadingElement : null}
<Sidebar />
{React.cloneElement(this.props.children, this.props)}
<Sidebar sensor={Object.assign({}, this.props.sensor)}/>
</div>
<Footer />
<Footer/>
</div>
);
}
}
const loadingElement = <div class="Loading">
<img src={loading} alt="loading..."/>
</div>;
);
}
}

View File

@@ -1,7 +1,13 @@
import * as types from "./constants";
import * as types from "../constants/app";
import marked from 'marked';
import 'whatwg-fetch';
export function increasePostLimit() {
return {
type: types.INCREASE_POST_LIMIT
}
}
function initPreview(posts) {
return {
type: types.INIT_PREVIEW,
@@ -9,15 +15,15 @@ function initPreview(posts) {
}
}
function loadPost(post){
function loadPost(post) {
return {
type: types.LOAD_POST,
post
type: types.LOAD_POST,
post
}
}
function fetching(){
return{
function fetching() {
return {
type: types.FETCHING
}
}

View 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);
});
}
}

View File

@@ -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';

View 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';

View 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';

View File

@@ -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 * as types from './constants';
import * as types from '../constants/app';
//defaults -
const defaultState = {
preview: {
posts: []
},
filteredPreview: {
posts: []
},
post: "",
fetched: false,
fetching: false
fetching: false,
postLimit: 10
};
//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
switch (action.type) {
case types.INIT_PREVIEW:
@@ -32,10 +22,6 @@ function reducer(state = defaultState, action) {
fetched: true,
fetching: false
});
case types.FILTER_PREVIEW:
return Object.assign({}, state, {
filteredPreview: Object.assign({}, state.filteredPreview, action.posts)
});
case types.LOAD_POST:
return Object.assign({}, state, {
post: action.post,
@@ -44,18 +30,15 @@ function reducer(state = defaultState, action) {
});
case types.FETCHING:
return Object.assign({}, state, {
fetched : false,
fetched: false,
fetching: true
});
case types.INCREASE_POST_LIMIT:
return Object.assign({}, state, {
postLimit: state.postLimit + 10
});
}
//return present state if no actions get called
return state;
}
const allReducers = combineReducers({
reducer,
routing: routerReducer
});
export default allReducers;

View 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;

View 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;
}

View File

@@ -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 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());

View File

@@ -40,8 +40,8 @@ function parse_dir(dir, folder_name){
filename: post.slice(0, post.length - 3),
category: folder_name,
date: post.slice(0, 10),
title: tokens[0].text,
intro: tokens[1].text
title: `<h1>${tokens[0].text}</h1>`,
intro: marked(tokens[1].text)
}
json.posts.push(temp);
}

View File

@@ -6,9 +6,12 @@
"scripts": {
"build": "webpack && babel-node metadata.js",
"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",
"prod": "export NODE_ENV=production && webpack -p && babel-node metadata.js",
"prod-win": "set NODE_ENV=production && webpack -p && babel-node metadata.js"
"get_dependencies": "go get ./server && npm install",
"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": {
"type": "git",

View 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.

View File

@@ -4,20 +4,17 @@
- Go App Engine
`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
- the posts folder is then copied into the dist folder
## TODO
- fix font sizing
- sticky footer
- clean up css
- adjust animations
- fix goapp serve and webpack-dev-server so paths work correctly
- posts page
- fix go and webpack-dev-server so paths work correctly
- 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
- finally do writeups on my projects

View File

@@ -11,7 +11,7 @@ func DiscordRedirect(w http.ResponseWriter, r *http.Request, _ httprouter.Params
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) {
http.Redirect(w, r, "https://mitchel.io:943", 301)
}

View File

@@ -34,6 +34,7 @@ func (s *Data) toJson() string {
}
//default store structure
func (s *Data) StoreData() error {
if db.Mongo.Connected() {
@@ -57,13 +58,13 @@ func (s *Data) StoreData() error {
}
//handle queries for all sensors page
//********************************************************************************
type DataStore_AllSensors struct {
ID string `json:"location" bson:"_id"`
Temperature float64 `json:"temperature" bson:"temperature"`
Updated time.Time `json:"updated" bson:"updated"`
}
//get latest update from each unique sensor
func GetAllSensors() ([]DataStore_AllSensors, error) {
s := []DataStore_AllSensors{}
@@ -88,52 +89,4 @@ func GetAllSensors() ([]DataStore_AllSensors, error) {
} else {
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(),
}
**************************/
}

View File

@@ -4,29 +4,14 @@ import (
"log"
"net/http"
"strconv"
//local import paths relative to app.yaml file
"github.com/NYTimes/gziphandler"
"mywebsite/server/controller/api"
"mywebsite/server/db"
"mywebsite/server/route"
"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(){
configurations := utils.ReadConfig()
@@ -34,7 +19,10 @@ func main(){
api.Configure(configurations.Api)
db.Mongo.Connect()
//register middleware
handle := gziphandler.GzipHandler(route.Routes())
log.Println("Starting Server...")
log.Println(http.ListenAndServe(":"+strconv.Itoa(configurations.Port), route.Routes()))
log.Println(http.ListenAndServe(":"+strconv.Itoa(configurations.Port), handle))
}

View File

@@ -14,7 +14,7 @@ import (
type Config struct {
Database db.DatabaseInfo `json:"database"`
Api api.ApiInfo `json:"api"`
Port int `json:"port"`
Port int `json:"port"`
}
//read the config file and return JsonObject struct
@@ -31,8 +31,6 @@ func ReadConfig() Config {
log.Printf("%s\n", string(file))
//m := new(Dispatch)
//var m interface{}
var result Config
err := json.Unmarshal(file, &result)