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

server set up with app engine

This commit is contained in:
2016-08-22 00:01:53 -05:00
parent 6dac5aeb06
commit 34b463cd44
18 changed files with 827 additions and 33 deletions

3
.gitignore vendored
View File

@@ -1,7 +1,6 @@
\.c9
\.DS_Store
config.json
mywebsite
mywebsite.exe
node_modules
dist
public

6
app.yaml Normal file
View File

@@ -0,0 +1,6 @@
runtime: go
api_version: go1
handlers:
- url: /.*
script: _go_app

View File

@@ -11,7 +11,7 @@ function initPreview(posts){
//in this case we can send the http request here rather in the react component
export function fetchPreview(){
return (dispatch) => {
return fetch('/metadata.json')
return fetch('/public/metadata.json')
.then(response => response.json())
.then(json => {
dispatch(initPreview(json));

9
config.template.json Normal file
View File

@@ -0,0 +1,9 @@
{
"database": {
"url": "",
"database": ""
},
"api": {
"key": ""
}
}

View File

@@ -1,5 +1,6 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
@@ -8,14 +9,23 @@
<link href="https://fonts.googleapis.com/css?family=Roboto+Slab:300,400" rel="stylesheet">
<title>mitchel.io</title>
</head>
<body>
<script>
(function(i, s, o, g, r, a, m) {i['GoogleAnalyticsObject'] = r;i[r] = i[r] || function() {
(i[r].q = i[r].q || []).push(arguments)}, i[r].l = 1 * new Date();
a = s.createElement(o),m = s.getElementsByTagName(o)[0];a.async = 1;a.src = g;m.parentNode.insertBefore(a, m)})
(function(i, s, o, g, r, a, m) {
i['GoogleAnalyticsObject'] = r;
i[r] = i[r] || function() {
(i[r].q = i[r].q || []).push(arguments)
}, i[r].l = 1 * new Date();
a = s.createElement(o), m = s.getElementsByTagName(o)[0];
a.async = 1;
a.src = g;
m.parentNode.insertBefore(a, m)
})
(window, document, 'script', 'https://www.google-analytics.com/analytics.js', 'ga');
</script>
<!--it's happening!!!-->
<div id="app"></div>
<!--it's happening!!!-->
<div id="app"></div>
</body>
</html>

View File

@@ -3,7 +3,7 @@
folder/files within posts are scanned recursively
each post is contained within category, which is supplied by the direct parent folder
Posts are sorted by date
Stores all metadata in ./dist/metadata.json
Stores all metadata in ./public/metadata.json
Client uses metadata to display posts on preview page
*/
@@ -58,13 +58,13 @@ json.posts.sort((a, b) => {
});
//output to public path
fs.writeFile('./dist/metadata.json', JSON.stringify(json,null,4), (err) => {
fs.writeFile('./public/metadata.json', JSON.stringify(json,null,4), (err) => {
if (err) throw err;
console.log("Saved metadata.json");
})
//copy posts folder to dist
ncp('./posts', './dist/posts', (err) => {
//copy posts folder to public
ncp('./posts', './public/posts', (err) => {
if (err) {
return console.error(err);
}

View File

@@ -5,8 +5,8 @@
"main": "index.js",
"scripts": {
"build": "webpack && babel-node metadata.js",
"c9": "webpack-dev-server --port $PORT --host $IP --hot --content-base dist --history-api-fallback",
"dev": "webpack-dev-server --content-base dist --inline --hot --history-api-fallback",
"c9": "webpack-dev-server --port $PORT --host $IP --hot --content-base public --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",
"prod-win": "set NODE_ENV=production && webpack -p && babel-node metadata.js",
"deploy": "goapp deploy",

View File

@@ -7,3 +7,13 @@
- 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 goapp serve and webpack-dev-server so paths work correctly
- posts 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

23
server.go Normal file
View File

@@ -0,0 +1,23 @@
package main
import (
"net/http"
"mywebsite/server/controller/api"
"mywebsite/server/db"
"mywebsite/server/route"
"mywebsite/server/utils"
)
func init() {
configurations := utils.ReadConfig()
db.Configure(configurations.Database)
api.Configure(configurations.Api)
db.Mongo.Connect()
router := route.Routes()
http.Handle("/", router)
}

View File

@@ -0,0 +1,13 @@
package api
import ()
var Api ApiInfo
type ApiInfo struct {
Key string `json:"key"`
}
func Configure(a ApiInfo) {
Api = a
}

View File

@@ -0,0 +1,211 @@
package api
import (
"encoding/json"
"fmt"
"github.com/julienschmidt/httprouter"
"log"
"net/http"
"strconv"
"time"
"mywebsite/server/model/daily_sensor"
"mywebsite/server/model/raw_sensor"
)
// handle http request from sensors
func HandleSensorRequest(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
//store data from sensor in raw_sensor collection
//**********************************************************************************
key := r.URL.Query().Get("key")
w.Header().Set("Content-Type", "application/json")
var message string
if key == Api.Key {
//get request parameters - convert temp to float64
temperature, _ := strconv.ParseFloat(r.URL.Query().Get("temperature"), 64)
location := r.URL.Query().Get("location")
t := time.Now()
store := raw_sensor.Data{"", temperature, location, t}
err := store.StoreData()
if err != nil {
message = "Failed to insert into database"
} else {
message = "Data inserted into database"
}
//compare current readings with dialy_sensor readings
//update daily_sensor readings if out of bounds
//**********************************************************************************
storedData, err := daily_sensor.GetDailySensorInfo(location)
if err != nil {
log.Println(err)
}
//store data if nothing exists for the day
if storedData.Location == "" {
storedData.ID = ""
storedData.Location = location
storedData.MaxTemp = temperature
storedData.MinTemp = temperature
storedData.Day = t.Day()
storedData.Month = int(t.Month())
storedData.MonthName = t.Month().String()
storedData.Year = t.Year()
err := storedData.StoreData()
if err != nil {
log.Println(err)
}
} else {
performUpdate := false
//check if values exceed max or min
if temperature > storedData.MaxTemp {
storedData.MaxTemp = temperature
performUpdate = true
}
if temperature < storedData.MinTemp {
storedData.MinTemp = temperature
performUpdate = true
}
//store or update information if values have been changed
if performUpdate == true {
err := storedData.UpdateData()
if err != nil {
log.Println(err)
}
}
}
} else {
message = "Incorrect api key"
}
//send response back
fmt.Fprint(w, "{ message : \""+message+"\"}")
}
func HandleAllSensors(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
w.Header().Set("Content-Type", "application/json")
s, err := raw_sensor.GetAllSensors()
var response string
if err != nil {
log.Println(err)
response = "{message : \"Error loading data from database\""
} else {
js, err := json.MarshalIndent(s, "", " ")
if err != nil {
log.Println(err)
response = "{message : \"Error loading data from database\""
} else {
response = string(js)
}
}
fmt.Fprint(w, response)
}
func HandleSensorByLocation(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
location := ps.ByName("location")
w.Header().Set("Content-Type", "application/json")
s, err := daily_sensor.GetAllSensorInfo(location)
var response string
if err != nil {
log.Println(err)
response = "{message : \"Error loading data from database\""
} else {
js, err := json.MarshalIndent(s, "", " ")
if err != nil {
log.Println(err)
response = "{message : \"Error loading data from database\""
} else {
response = string(js)
}
}
fmt.Fprint(w, response)
}
func HandleSensorByLocationYear(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
location := ps.ByName("location")
year, _ := strconv.Atoi(ps.ByName("year"))
w.Header().Set("Content-Type", "application/json")
s, err := daily_sensor.GetAllSensorInfoByYear(location, year)
var response string
if err != nil {
log.Println(err)
response = "{message : \"Error loading data from database\""
} else {
js, err := json.MarshalIndent(s, "", " ")
if err != nil {
log.Println(err)
response = "{message : \"Error loading data from database\""
} else {
response = string(js)
}
}
fmt.Fprint(w, response)
}
func HandleSensorByLocationMonth(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
location := ps.ByName("location")
year, _ := strconv.Atoi(ps.ByName("year"))
monthname := ps.ByName("monthname")
w.Header().Set("Content-Type", "application/json")
s, err := daily_sensor.GetAllSensorInfoByMonth(location, year, monthname)
var response string
if err != nil {
log.Println(err)
response = "{message : \"Error loading data from database\""
} else {
js, err := json.MarshalIndent(s, "", " ")
if err != nil {
log.Println(err)
response = "{message : \"Error loading data from database\""
} else {
response = string(js)
}
}
fmt.Fprint(w, response)
}

View File

@@ -0,0 +1,22 @@
package controller
import (
"github.com/julienschmidt/httprouter"
"net/http"
)
// redirects for personal use
// Redirect to discord
func DiscordRedirect(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
http.Redirect(w, r, "https://discordapp.com/invite/0Z2tzxKECEj2BHwj", 301)
}
// Redirect to discord
func VPNRedirect(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
http.Redirect(w, r, "https://mitchel.io:943", 301)
}
// Redirect to security cameras
func CameraRedirect(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
http.Redirect(w, r, "http://24.118.44.161:8080/html/", 301)
}

54
server/db/db.go Normal file
View File

@@ -0,0 +1,54 @@
package db
import (
"gopkg.in/mgo.v2"
"log"
"time"
)
var Mongo Driver
type Driver struct {
Session *mgo.Session
Info DatabaseInfo
}
type DatabaseInfo struct {
URL string `json:"url"`
Database string `json:"database"`
Username string `json:"username,omitempty"`
Password string `json:"password,omitempty"`
}
func Configure(d DatabaseInfo) {
Mongo.Info = d
}
func (d *Driver) Connect() {
// Connect to MongoDB
s, err := mgo.DialWithTimeout(d.Info.URL, 5*time.Second)
if err != nil {
log.Println("MongoDB Driver Error", err)
return
}
d.Session = s
// Prevents these errors: read tcp 127.0.0.1:27017: i/o timeout
d.Session.SetSocketTimeout(10 * time.Second)
// Check if is alive
if err = d.Session.Ping(); err != nil {
log.Println("Database Error", err)
}
log.Println("Connected to database")
}
func (d *Driver) Connected() bool {
if d.Session != nil {
return true
}
return false
}

View File

@@ -0,0 +1,200 @@
package daily_sensor
import (
"encoding/json"
"errors"
"gopkg.in/mgo.v2/bson"
"log"
"time"
"mywebsite/server/db"
)
const (
collection = "daily_sensor"
)
type Data struct {
ID bson.ObjectId `bson:"_id,omitempty"`
MaxTemp float64 `json:"maxtemp" bson:"maxtemp"`
MinTemp float64 `json:"mintemp" bson:"mintemp"`
Location string `json:"location" bson:"location"`
Month int `json:"month" bson:"month"`
MonthName string `json:"monthname" bson:"monthname"`
Day int `json:"day" bson:"day"`
Year int `json:"year" bson:"year"`
}
//convert struct to json string
func (s *Data) toJson() string {
b, err := json.MarshalIndent(s, "", " ")
if err != nil {
log.Println(err.Error)
}
return string(b)
}
func (s *Data) StoreData() error {
if db.Mongo.Connected() {
log.Println("Inserting data into " + collection)
session := db.Mongo.Session.Copy()
defer session.Close()
c := session.DB(db.Mongo.Info.Database).C(collection)
// Insert Datas
err := c.Insert(s)
if err != nil {
return err
}
}
return nil
}
//function to update the daily temperature max or min for a location
func (s *Data) UpdateData() error {
if db.Mongo.Connected() {
log.Println("Updating data")
session := db.Mongo.Session.Copy()
defer session.Close()
c := session.DB(db.Mongo.Info.Database).C(collection)
colQuerier := bson.M{"location": s.Location, "month": s.Month, "monthname": s.MonthName, "day": s.Day, "year": s.Year}
change := bson.M{"$set": bson.M{"maxtemp": s.MaxTemp, "mintemp": s.MinTemp}}
err := c.Update(colQuerier, change)
if err != nil {
return err
}
}
return nil
}
//get the current daily sensor reading to compare
//if the max or min temp needs to be updated
func GetDailySensorInfo(sensor_location string) (Data, error) {
d := Data{}
t := time.Now()
day := t.Day()
month := int(t.Month())
monthname := t.Month().String()
year := t.Year()
if db.Mongo.Connected() == true {
session := db.Mongo.Session.Copy()
defer session.Close()
c := session.DB(db.Mongo.Info.Database).C(collection)
err := c.Find(bson.M{"location": sensor_location, "day": day, "month": month, "monthname": monthname, "year": year}).One(&d)
if err != nil {
log.Println(err)
return d, nil
}
return d, nil
} else {
return d, errors.New("Query failed")
}
}
func GetAllSensorInfo(sensor_location string) ([]Data, error) {
d := []Data{}
if db.Mongo.Connected() == true {
session := db.Mongo.Session.Copy()
defer session.Close()
c := session.DB(db.Mongo.Info.Database).C(collection)
//err := c.Find(bson.M{"location": sensor_location}).Sort("-year, -month").All(&d)
err := c.Pipe([]bson.M{{"$match": bson.M{"location": sensor_location}},
{"$sort": bson.M{"year": -1, "month": 1}}}).All(&d)
if err != nil {
log.Println(err)
return d, nil
}
return d, nil
} else {
return d, errors.New("Query failed")
}
}
//return all daily temperature readings per year for a location
//sort by most recent year and oldest month
func GetAllSensorInfoByYear(sensor_location string, year int) ([]Data, error) {
d := []Data{}
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{{"$match": bson.M{"location": sensor_location, "year": year}},
{"$sort": bson.M{"year": -1, "month": 1}}}).All(&d)
if err != nil {
log.Println(err)
return d, nil
}
return d, nil
} else {
return d, errors.New("Query failed")
}
}
//return all temperature readings for a specific month and location
func GetAllSensorInfoByMonth(sensor_location string, year int, monthname string) ([]Data, error) {
d := []Data{}
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{{"$match": bson.M{"location": sensor_location, "year": year, "monthname": monthname}},
{"$sort": bson.M{"year": -1, "month": 1}}}).All(&d)
if err != nil {
log.Println(err)
return d, nil
}
return d, nil
} else {
return d, errors.New("Query failed")
}
}

View File

@@ -0,0 +1,139 @@
package raw_sensor
import (
"encoding/json"
"errors"
"gopkg.in/mgo.v2/bson"
"log"
"time"
"mywebsite/server/db"
)
const (
collection = "raw_sensor"
)
type Data struct {
ID bson.ObjectId `bson:"_id,omitempty"`
Temperature float64 `json:"temperature" bson:"temperature"`
Location string `json:"location" bson:"location"`
Updated time.Time `json:"updated" bson:"updated"`
}
//convert struct to json string
func (s *Data) toJson() string {
b, err := json.MarshalIndent(s, "", " ")
if err != nil {
log.Println(err.Error)
}
return string(b)
}
func (s *Data) StoreData() error {
if db.Mongo.Connected() {
log.Println("Inserting data into " + collection)
session := db.Mongo.Session.Copy()
defer session.Close()
c := session.DB(db.Mongo.Info.Database).C(collection)
// Insert Datas
err := c.Insert(s)
if err != nil {
return err
}
}
return nil
}
//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"`
}
func GetAllSensors() ([]DataStore_AllSensors, error) {
s := []DataStore_AllSensors{}
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{{"$group": bson.M{"_id": "$location", "temperature": bson.M{"$last": "$temperature"},
"updated": bson.M{"$last": "$updated"}}},
bson.M{"$sort": bson.M{"_id": 1}}}).All(&s)
if err != nil {
return s, nil
}
return s, nil
} 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(),
}
**************************/

54
server/route/route.go Normal file
View File

@@ -0,0 +1,54 @@
package route
import (
"github.com/julienschmidt/httprouter"
"log"
"net/http"
"mywebsite/server/controller"
"mywebsite/server/controller/api"
)
func Routes() *httprouter.Router {
log.Println("Server Started")
r := httprouter.New()
r.GET("/api/storedata", api.HandleSensorRequest)
r.GET("/api/allsensors", api.HandleAllSensors)
r.GET("/api/sensor/:location", api.HandleSensorByLocation)
r.GET("/api/sensor/:location/:year", api.HandleSensorByLocationYear)
r.GET("/api/sensor/:location/:year/:monthname", api.HandleSensorByLocationMonth)
r.GET("/discord", controller.DiscordRedirect)
r.GET("/vpn", controller.VPNRedirect)
r.GET("/camera", controller.CameraRedirect)
//set up public folder path
r.ServeFiles("/public/*filepath", http.Dir("./public/"))
//route every invalid request to template file
//routing is all handled on the client side with angular
r.NotFound = http.HandlerFunc(fileHandler("./public/index.html"))
return r
}
//route requests to static files
func routerFileHandler(path string) httprouter.Handle {
return func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
http.ServeFile(w, r, path)
}
}
//function to serve files with standard net/http library
func fileHandler(path string) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
http.ServeFile(w, r, path)
}
}

44
server/utils/config.go Normal file
View File

@@ -0,0 +1,44 @@
package utils
import (
"encoding/json"
"io/ioutil"
"log"
"os"
"mywebsite/server/controller/api"
"mywebsite/server/db"
)
//structure for application configurations
type Config struct {
Database db.DatabaseInfo `json:"database"`
Api api.ApiInfo `json:"api"`
}
//read the config file and return JsonObject struct
func ReadConfig() Config {
log.Println("Reading config file...")
file, e := ioutil.ReadFile("./config.json")
if e != nil {
log.Printf("File error: %v\n", e)
os.Exit(1)
}
log.Printf("%s\n", string(file))
//m := new(Dispatch)
//var m interface{}
var result Config
err := json.Unmarshal(file, &result)
if err != nil {
log.Println(err)
}
return result
}

View File

@@ -33,8 +33,8 @@ module.exports = {
]
},
output: {
path: __dirname + "/dist/",
publicPath: "/dist/",
path: __dirname + "/public/",
publicPath: "/public/",
filename: "client.min.js"
},
plugins: debug ? [] : [