diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/go-location-logger.iml b/.idea/go-location-logger.iml new file mode 100644 index 0000000..5e764c4 --- /dev/null +++ b/.idea/go-location-logger.iml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..703fe63 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..aff40c1 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,22 @@ +# Use an official Golang runtime as a parent image +FROM golang:1.21.4 + +# Set the working directory in the container +WORKDIR /go/src/app + +# Copy the local package files to the container's workspace +COPY ../../Downloads . + +# Download and install any required third-party dependencies into the container. +#RUN go get -u github.com/gorilla/mux +RUN go get -u github.com/go-sql-driver/mysql +RUN go get -u github.com/sirupsen/logrus + +# Build the Go application +RUN go build -o main . + +# Expose port 8080 to the outside world +EXPOSE 8080 + +# Command to run the application with environment variables +CMD ["./main"] diff --git a/README.md b/README.md index c8e1416..58a30ad 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,98 @@ -# go-location-logger +# Go Location Logger +A simple Go program that handles location data sent via HTTP POST requests and inserts it into a MySQL database. + +## Features + +- Handles JSON payloads containing location data. +- Inserts location data into a MySQL database. +- Supports parameters `device` and `user` in HTTP requests. + +## Prerequisites + +Before running the program, ensure you have the following: + +- Go installed: [https://golang.org/doc/install](https://golang.org/doc/install) +- Docker and Docker Compose installed: [https://docs.docker.com/get-docker/](https://docs.docker.com/get-docker/) + +## Installation + +1. Clone the repository: + + ```bash + git clone https://github.com/yourusername/go-location-logger.git + ``` + +2. Navigate to the project directory: + + ```bash + cd go-location-logger + ``` + +3. Install dependencies: + + ```bash + go get -u github.com/sirupsen/logrus + ``` + +4. Create an `.env` file in the project root with your environment variables: + + ```plaintext + # .env + + DB_USER=your_db_user + DB_PASSWORD=your_db_password + DB_HOST=your_db_host + DB_PORT=your_db_port + DB_NAME=your_db_name + ``` + +## Usage + +### Running with Docker Compose + +1. Create a Docker network: + + ```bash + docker network create app-network + ``` + +2. Use Docker Compose to start the services: + + ```bash + docker-compose up -d + ``` + +3. Access your Go application at `http://localhost:8080`. Make HTTP POST requests as described in the README.md. + +4. To stop the services, run: + + ```bash + docker-compose down + ``` + +### Running without Docker + +If you prefer not to use Docker, you can run the Go application directly using: + +```bash +go run main.go +``` + +The server will start on `http://localhost:8080`. + +Make a POST request with a JSON payload to `http://localhost:8080/?device=your_device&user=your_user`. The JSON payload should include location data. + +Example: + +```bash +curl -X POST -d '{"_type": "location", "tst": 1637650367, "lat": 37.7749, "lon": -122.4194, "tid": "123", "batt": 90, "vac": 220}' http://localhost:8080/?device=your_device&user=your_user +``` + +## Contributing + +Feel free to open issues or submit pull requests. + +## License + +This project is licensed under the MIT License - see the [LICENSE.md](LICENSE.md) file for details. \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..74892dc --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,34 @@ +version: "3.9" + +services: + +# Go application service + go-app: + build: + context: go/. + args: + - GO111MODULE=on + ports: + - "8080:8080" + depends_on: + - db + environment: + - DB_HOST=db + - DB_PORT=3306 + - DB_USER=root + - DB_PASSWORD=owntracks + - DB_NAME=owntracks + + restart: always + networks: + default: + + db: + image: mariadb + volumes: + - ./mysql-data:/var/lib/mysql + environment: + MYSQL_ROOT_PASSWORD: owntracks + MYSQL_DATABASE: owntracks + networks: + default: \ No newline at end of file diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..119d48a --- /dev/null +++ b/go.mod @@ -0,0 +1,10 @@ +module go-app + +go 1.21.4 + +require ( + github.com/go-sql-driver/mysql v1.7.1 + github.com/sirupsen/logrus v1.9.3 +) + +require golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 // indirect diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..05471c9 --- /dev/null +++ b/go.sum @@ -0,0 +1,17 @@ +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI= +github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 h1:0A+M6Uqn+Eje4kHMK80dtF3JCXC4ykBgQG4Fe06QRhQ= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/main.go b/main.go new file mode 100644 index 0000000..50fa232 --- /dev/null +++ b/main.go @@ -0,0 +1,110 @@ +package main + +import ( + "database/sql" + "encoding/json" + "fmt" + "net/http" + "os" + "time" + + _ "github.com/go-sql-driver/mysql" + "github.com/sirupsen/logrus" +) + +var log = logrus.New() + +// Location represents the JSON payload structure +type Location struct { + Type string `json:"_type"` + Tst int64 `json:"tst"` + Lat float64 `json:"lat"` + Lon float64 `json:"lon"` + Tid string `json:"tid"` + Batt int `json:"batt"` + Vac int `json:"vac"` +} + +func init() { + // Log as JSON instead of the default ASCII formatter. + log.SetFormatter(&logrus.JSONFormatter{}) + + // Output to stdout instead of the default stderr + // Can be any io.Writer, see below for File example + log.SetOutput(os.Stdout) + + // Only log the warning severity or above. + log.SetLevel(logrus.InfoLevel) +} + +func main() { + log.Info("Server started. Listening on :8080") + + http.HandleFunc("/", handleRequest) + http.ListenAndServe(":8080", nil) +} + +func handleRequest(w http.ResponseWriter, r *http.Request) { + log.Info("Handling request") + + if r.Method != http.MethodPost { + log.Warn("Method not allowed") + http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) + return + } + + // Parse query parameters + device := r.URL.Query().Get("d") + user := r.URL.Query().Get("u") + + log.Infof("Received request with device=%s and user=%s", device, user) + + var loc Location + decoder := json.NewDecoder(r.Body) + err := decoder.Decode(&loc) + if err != nil { + log.Error("Invalid JSON payload: ", err) + http.Error(w, "Invalid JSON payload", http.StatusBadRequest) + return + } + + log.Infof("Received JSON payload: %+v", loc) + + if loc.Type == "location" { + db, err := sql.Open("mysql", getDBConnectionString()) + if err != nil { + log.Error("Database connection error: ", err) + http.Error(w, fmt.Sprintf("Database connection error: %v", err), http.StatusInternalServerError) + return + } + defer db.Close() + + dt := time.Unix(loc.Tst, 0).Format("2006-01-02 15:04:05") + + _, err = db.Exec("INSERT INTO locations (dt, tid, lat, lon, batt, vac, device, user) VALUES (?, ?, ?, ?, ?, ?, ?, ?)", + dt, loc.Tid, loc.Lat, loc.Lon, loc.Batt, loc.Vac, device, user) + if err != nil { + log.Error("Database insertion error: ", err) + http.Error(w, fmt.Sprintf("Database insertion error: %v", err), http.StatusInternalServerError) + return + } + + log.Info("Location data inserted into the database") + } + + response := make(map[string]interface{}) + // Optionally add objects to return to the app (e.g., friends or cards) + json.NewEncoder(w).Encode(response) + + log.Info("Request handled successfully") +} + +func getDBConnectionString() string { + user := os.Getenv("DB_USER") + password := os.Getenv("DB_PASSWORD") + host := os.Getenv("DB_HOST") + port := os.Getenv("DB_PORT") + dbName := os.Getenv("DB_NAME") + + return fmt.Sprintf("%s:%s@tcp(%s:%s)/%s", user, password, host, port, dbName) +}