From 93dca9b30ba86c1ff482758261b29cff8bc0c67c Mon Sep 17 00:00:00 2001 From: norohind <60548839+norohind@users.noreply.github.com> Date: Mon, 8 Apr 2024 22:22:15 +0300 Subject: [PATCH] init --- .dockerignore | 5 ++++ .gitignore | 2 ++ Dockerfile | 32 +++++++++++++++++++++ README.md | 19 ++++++++++++ config.go | 34 ++++++++++++++++++++++ go.mod | 21 ++++++++++++++ go.sum | 41 ++++++++++++++++++++++++++ main.go | 80 +++++++++++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 234 insertions(+) create mode 100644 .dockerignore create mode 100644 .gitignore create mode 100644 Dockerfile create mode 100644 README.md create mode 100644 config.go create mode 100644 go.mod create mode 100644 go.sum create mode 100644 main.go diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..bf9ccb1 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,5 @@ +requests.db +.idea +Dockerfile +.dockerignore +README.md diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7d26822 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +requests.db +.idea diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..5ee4a9a --- /dev/null +++ b/Dockerfile @@ -0,0 +1,32 @@ +# Start from a Golang base image +FROM docker.io/golang:1.22-alpine AS builder + +# Set the Current Working Directory inside the container +WORKDIR /app + +# Copy go mod and sum files +COPY go.mod go.sum ./ + +# Download all dependencies. Dependencies will be cached if the go.mod and go.sum files are not changed +RUN go mod download + +# Copy the source code into the container +COPY . . + +# Build the Go app +RUN go build -o main . + +# Start a new stage from scratch +FROM alpine:latest + +WORKDIR /app + +# Copy the Pre-built binary file from the previous stage +COPY --from=builder /app/main /app/scrobblesRecorder + +RUN mkdir /data && chown 1000:1000 /data + +USER 1000 + +# Command to run the executable +CMD ["/app/scrobblesRecorder"] diff --git a/README.md b/README.md new file mode 100644 index 0000000..97561ae --- /dev/null +++ b/README.md @@ -0,0 +1,19 @@ +# Go HTTP Server with SQLite Database Recorder + +This program is a simple HTTP server written in Go that records incoming requests to a SQLite database. It captures the URL, HTTP method, request body (if any), and timestamp of each request. + +## How It Works + +The server listens for incoming HTTP requests on port 8080 by default. When a request is received, it logs the request details to a SQLite database located at `/data/database.sqlite`. The database schema includes fields for the URL, HTTP method, request body, and timestamp of each request. + +## Configuration + +You can configure the program using environment variables. The available configuration options are: + +- `DB_PATH`: Path to the SQLite database file. Default value is `/data/database.sqlite`. +- `PORT`: Port number for the HTTP server to listen on. Default value is `8080`. + +You can modify these options by setting the corresponding environment variables. + +Make sure to put database file under `/data/` directory because the process runs under 1000 UID user +and `/data/` mount point has ownership of 1000 UID user. \ No newline at end of file diff --git a/config.go b/config.go new file mode 100644 index 0000000..5fd81e1 --- /dev/null +++ b/config.go @@ -0,0 +1,34 @@ +package main + +import ( + "os" + "strconv" +) + +type Config struct { + DbPath string + Port int +} + +func New() *Config { + return &Config{ + DbPath: getEnv("DB_PATH", "/data/database.sqlite"), + Port: getEnvAsInt("PORT", 8080), + } +} + +func getEnv(key string, defaultVal string) string { + if value, exists := os.LookupEnv(key); exists { + return value + } + return defaultVal +} + +func getEnvAsInt(name string, defaultVal int) int { + valueStr := getEnv(name, "") + if value, err := strconv.Atoi(valueStr); err == nil { + return value + } + + return defaultVal +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..9d5aec1 --- /dev/null +++ b/go.mod @@ -0,0 +1,21 @@ +module scrobblesRecorder + +go 1.22.0 + +require modernc.org/sqlite v1.29.6 + +require ( + github.com/dustin/go-humanize v1.0.1 // indirect + github.com/google/uuid v1.3.0 // indirect + github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect + github.com/mattn/go-isatty v0.0.16 // indirect + github.com/ncruces/go-strftime v0.1.9 // indirect + github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect + golang.org/x/sys v0.16.0 // indirect + modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 // indirect + modernc.org/libc v1.41.0 // indirect + modernc.org/mathutil v1.6.0 // indirect + modernc.org/memory v1.7.2 // indirect + modernc.org/strutil v1.2.0 // indirect + modernc.org/token v1.1.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..017bdab --- /dev/null +++ b/go.sum @@ -0,0 +1,41 @@ +github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= +github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= +github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ= +github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= +github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= +github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= +github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= +github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4= +github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= +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/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= +github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= +golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= +golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= +golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/tools v0.17.0 h1:FvmRgNOcs3kOa+T20R1uhfP9F6HgG2mfxDv1vrx1Htc= +golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps= +modernc.org/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE= +modernc.org/fileutil v1.3.0/go.mod h1:XatxS8fZi3pS8/hKG2GH/ArUogfxjpEKs3Ku3aK4JyQ= +modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 h1:5D53IMaUuA5InSeMu9eJtlQXS2NxAhyWQvkKEgXZhHI= +modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6/go.mod h1:Qz0X07sNOR1jWYCrJMEnbW/X55x206Q7Vt4mz6/wHp4= +modernc.org/libc v1.41.0 h1:g9YAc6BkKlgORsUWj+JwqoB1wU3o4DE3bM3yvA3k+Gk= +modernc.org/libc v1.41.0/go.mod h1:w0eszPsiXoOnoMJgrXjglgLuDy/bt5RR4y3QzUUeodY= +modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4= +modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo= +modernc.org/memory v1.7.2 h1:Klh90S215mmH8c9gO98QxQFsY+W451E8AnzjoE2ee1E= +modernc.org/memory v1.7.2/go.mod h1:NO4NVCQy0N7ln+T9ngWqOQfi7ley4vpwvARR+Hjw95E= +modernc.org/sqlite v1.29.6 h1:0lOXGrycJPptfHDuohfYgNqoe4hu+gYuN/pKgY5XjS4= +modernc.org/sqlite v1.29.6/go.mod h1:S02dvcmm7TnTRvGhv8IGYyLnIt7AS2KPaB1F/71p75U= +modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA= +modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0= +modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= +modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= diff --git a/main.go b/main.go new file mode 100644 index 0000000..1501b2a --- /dev/null +++ b/main.go @@ -0,0 +1,80 @@ +package main + +import ( + "database/sql" + "fmt" + "io" + "log" + _ "modernc.org/sqlite" + "net/http" + "strconv" +) + +var db *sql.DB + +func main() { + // Open the database file + var err error + var config = New() + + db, err = sql.Open("sqlite", config.DbPath) + if err != nil { + log.Fatal(err) + } + defer func(db *sql.DB) { + err := db.Close() + if err != nil { + log.Fatal(err) + } + }(db) + + // Create requests table if it doesn't exist + _, err = db.Exec(`CREATE TABLE IF NOT EXISTS requests ( + remote_addr TEXT, + url TEXT, + method TEXT, + authorization TEXT, + body TEXT, + timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP + )`) + if err != nil { + log.Fatal(err) + } + + // Setup HTTP server + http.HandleFunc("/", logRequest) + fmt.Println("Server listening on port", strconv.Itoa(config.Port)) + err = http.ListenAndServe(":"+strconv.Itoa(config.Port), nil) + if err != nil { + log.Fatal(err) + } +} + +func logRequest(w http.ResponseWriter, r *http.Request) { + // Read request body + body := "" + if r.Body != nil { + defer func(Body io.ReadCloser) { + err := Body.Close() + if err != nil { + log.Fatal(err) + } + }(r.Body) + buf := make([]byte, 1024) + n, _ := r.Body.Read(buf) + body = string(buf[:n]) + } + + // Insert request data into database + fmt.Println(r.RemoteAddr, r.URL.String(), r.Method, r.Header.Get("Authorization"), body) + _, err := db.Exec("INSERT INTO requests (remote_addr, url, method, authorization, body) VALUES (?, ?, ?, ?, ?)", + r.RemoteAddr, r.URL.String(), r.Method, r.Header.Get("Authorization"), body) + + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + // Respond to the request + w.WriteHeader(http.StatusOK) +}