From 93464d9a66770e2c9ebf2414ef0d46c50959f364 Mon Sep 17 00:00:00 2001 From: Mitchell Date: Sun, 28 Aug 2016 22:40:30 -0500 Subject: [PATCH] merging back into one repo - not going to host on app engine - google won't allow me to connect to external database --- .gitignore | 1 + config.template.json | 12 ++ server.go | 40 ++++ server/controller/api/config.go | 13 ++ server/controller/api/sensor.go | 211 ++++++++++++++++++++++ server/controller/redirect.go | 22 +++ server/db/db.go | 66 +++++++ server/model/daily_sensor/daily_sensor.go | 200 ++++++++++++++++++++ server/model/raw_sensor/raw_sensor.go | 139 ++++++++++++++ server/route/route.go | 54 ++++++ server/utils/config.go | 45 +++++ 11 files changed, 803 insertions(+) create mode 100644 config.template.json create mode 100644 server.go create mode 100644 server/controller/api/config.go create mode 100644 server/controller/api/sensor.go create mode 100644 server/controller/redirect.go create mode 100644 server/db/db.go create mode 100644 server/model/daily_sensor/daily_sensor.go create mode 100644 server/model/raw_sensor/raw_sensor.go create mode 100644 server/route/route.go create mode 100644 server/utils/config.go diff --git a/.gitignore b/.gitignore index c5556ad..3815327 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ node_modules dist public npm-debug.log +mywebsite diff --git a/config.template.json b/config.template.json new file mode 100644 index 0000000..a987565 --- /dev/null +++ b/config.template.json @@ -0,0 +1,12 @@ +{ + "database": { + "url": "", + "database": "", + "username": "", + "password": "" + }, + "api": { + "key": "" + }, + "port": 8080 +} diff --git a/server.go b/server.go new file mode 100644 index 0000000..9377d20 --- /dev/null +++ b/server.go @@ -0,0 +1,40 @@ +package main + +import ( + "log" + "net/http" + "strconv" + + //local import paths relative to app.yaml file + "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() + + db.Configure(configurations.Database) + api.Configure(configurations.Api) + + db.Mongo.Connect() + + log.Println("Starting Server...") + log.Println(http.ListenAndServe(":"+strconv.Itoa(configurations.Port), route.Routes())) +} diff --git a/server/controller/api/config.go b/server/controller/api/config.go new file mode 100644 index 0000000..f9b34fc --- /dev/null +++ b/server/controller/api/config.go @@ -0,0 +1,13 @@ +package api + +import () + +var Api ApiInfo + +type ApiInfo struct { + Key string `json:"key"` +} + +func Configure(a ApiInfo) { + Api = a +} diff --git a/server/controller/api/sensor.go b/server/controller/api/sensor.go new file mode 100644 index 0000000..5dd87be --- /dev/null +++ b/server/controller/api/sensor.go @@ -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) +} diff --git a/server/controller/redirect.go b/server/controller/redirect.go new file mode 100644 index 0000000..0b78f0f --- /dev/null +++ b/server/controller/redirect.go @@ -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) +} diff --git a/server/db/db.go b/server/db/db.go new file mode 100644 index 0000000..9759426 --- /dev/null +++ b/server/db/db.go @@ -0,0 +1,66 @@ +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() { + if (d.Info.URL != ""){ + // Connect to MongoDB + database_info := &mgo.DialInfo{ + Addrs: []string{d.Info.URL}, + Database: d.Info.Database, + Timeout: 5*time.Second, + Username: d.Info.Username, + Password: d.Info.Password, + } + + s, err := mgo.DialWithInfo(database_info) + + 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") + } else { + log.Println("Database not configured") + } +} + +func (d *Driver) Connected() bool { + if d.Session != nil { + return true + } + return false +} diff --git a/server/model/daily_sensor/daily_sensor.go b/server/model/daily_sensor/daily_sensor.go new file mode 100644 index 0000000..99837bb --- /dev/null +++ b/server/model/daily_sensor/daily_sensor.go @@ -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") + } +} diff --git a/server/model/raw_sensor/raw_sensor.go b/server/model/raw_sensor/raw_sensor.go new file mode 100644 index 0000000..c329a09 --- /dev/null +++ b/server/model/raw_sensor/raw_sensor.go @@ -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(), +} +**************************/ diff --git a/server/route/route.go b/server/route/route.go new file mode 100644 index 0000000..8d7b9a6 --- /dev/null +++ b/server/route/route.go @@ -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) + + } +} diff --git a/server/utils/config.go b/server/utils/config.go new file mode 100644 index 0000000..4b60f40 --- /dev/null +++ b/server/utils/config.go @@ -0,0 +1,45 @@ +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"` + Port int `json:"port"` +} + +//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 +}