;
+ );
+ }
+}
\ No newline at end of file
diff --git a/client/js/redux/actions.js b/client/js/redux/actions/app.js
similarity index 81%
rename from client/js/redux/actions.js
rename to client/js/redux/actions/app.js
index 47adfdd..b1c9018 100644
--- a/client/js/redux/actions.js
+++ b/client/js/redux/actions/app.js
@@ -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
}
}
diff --git a/client/js/redux/actions/sensor.js b/client/js/redux/actions/sensor.js
new file mode 100644
index 0000000..e356e99
--- /dev/null
+++ b/client/js/redux/actions/sensor.js
@@ -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);
+ });
+ }
+}
\ No newline at end of file
diff --git a/client/js/redux/constants.js b/client/js/redux/constants.js
deleted file mode 100644
index d9cc693..0000000
--- a/client/js/redux/constants.js
+++ /dev/null
@@ -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';
\ No newline at end of file
diff --git a/client/js/redux/constants/app.js b/client/js/redux/constants/app.js
new file mode 100644
index 0000000..5eb537f
--- /dev/null
+++ b/client/js/redux/constants/app.js
@@ -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';
\ No newline at end of file
diff --git a/client/js/redux/constants/sensor.js b/client/js/redux/constants/sensor.js
new file mode 100644
index 0000000..ae93469
--- /dev/null
+++ b/client/js/redux/constants/sensor.js
@@ -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';
\ No newline at end of file
diff --git a/client/js/redux/reducers.js b/client/js/redux/reducers/app.js
similarity index 61%
rename from client/js/redux/reducers.js
rename to client/js/redux/reducers/app.js
index 37bfec8..5611138 100644
--- a/client/js/redux/reducers.js
+++ b/client/js/redux/reducers/app.js
@@ -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;
diff --git a/client/js/redux/reducers/reducers.js b/client/js/redux/reducers/reducers.js
new file mode 100644
index 0000000..5e5d7bd
--- /dev/null
+++ b/client/js/redux/reducers/reducers.js
@@ -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;
\ No newline at end of file
diff --git a/client/js/redux/reducers/sensor.js b/client/js/redux/reducers/sensor.js
new file mode 100644
index 0000000..8d5a4af
--- /dev/null
+++ b/client/js/redux/reducers/sensor.js
@@ -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;
+}
diff --git a/client/js/redux/store.js b/client/js/redux/store.js
index fc5dc04..b28f24c 100644
--- a/client/js/redux/store.js
+++ b/client/js/redux/store.js
@@ -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());
diff --git a/metadata.js b/metadata.js
index 6762140..fa3bce8 100644
--- a/metadata.js
+++ b/metadata.js
@@ -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: `
${tokens[0].text}
`,
+ intro: marked(tokens[1].text)
}
json.posts.push(temp);
}
diff --git a/package.json b/package.json
index b0cfce2..72b6bf9 100644
--- a/package.json
+++ b/package.json
@@ -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",
diff --git a/posts/Web Stuff/2016-08-30-ubuntu16-mongodb.md b/posts/Web Stuff/2016-08-30-ubuntu16-mongodb.md
new file mode 100644
index 0000000..57b2997
--- /dev/null
+++ b/posts/Web Stuff/2016-08-30-ubuntu16-mongodb.md
@@ -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
+ sudo systemctl 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 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.
\ No newline at end of file
diff --git a/readme.md b/readme.md
index 0e7b72b..7f0b8fc 100644
--- a/readme.md
+++ b/readme.md
@@ -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
\ No newline at end of file
diff --git a/server/controller/redirect.go b/server/controller/redirect.go
index 0b78f0f..35299bd 100644
--- a/server/controller/redirect.go
+++ b/server/controller/redirect.go
@@ -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)
}
diff --git a/server/model/raw_sensor/raw_sensor.go b/server/model/raw_sensor/raw_sensor.go
index c329a09..9b91b54 100644
--- a/server/model/raw_sensor/raw_sensor.go
+++ b/server/model/raw_sensor/raw_sensor.go
@@ -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(),
-}
-**************************/
+}
\ No newline at end of file
diff --git a/server.go b/server/mywebsite.go
similarity index 57%
rename from server.go
rename to server/mywebsite.go
index 9377d20..edb23b2 100644
--- a/server.go
+++ b/server/mywebsite.go
@@ -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))
}
diff --git a/server/utils/config.go b/server/utils/config.go
index 4b60f40..5085ad6 100644
--- a/server/utils/config.go
+++ b/server/utils/config.go
@@ -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)