This commit is contained in:
norohind 2024-04-08 22:22:15 +03:00
commit 93dca9b30b
8 changed files with 234 additions and 0 deletions

5
.dockerignore Normal file
View File

@ -0,0 +1,5 @@
requests.db
.idea
Dockerfile
.dockerignore
README.md

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
requests.db
.idea

32
Dockerfile Normal file
View File

@ -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"]

19
README.md Normal file
View File

@ -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.

34
config.go Normal file
View File

@ -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
}

21
go.mod Normal file
View File

@ -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
)

41
go.sum Normal file
View File

@ -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=

80
main.go Normal file
View File

@ -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)
}