Skip to content

Commit c694447

Browse files
Database and api (#1)
* Adds intial support for database * Adds option to enable database and api. * Update Go version in go.mod * Revise proxy server start commands in README Updated commands to start the proxy server. * Refactor port flag parsing in main function Moved port flag parsing to the beginning of the main function. --------- Co-authored-by: Holger Wolff <waringer@users.noreply.github.com>
1 parent 6873271 commit c694447

7 files changed

Lines changed: 207 additions & 13 deletions

File tree

.gitignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,9 @@
33
*.code-workspace
44
**/bin/
55
**/obj/
6+
7+
# App specific ignores
8+
emoProxy.conf
9+
data/
10+
# tmp is used for air
11+
tmp/

README.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@
22
This part of the knowledge bank is a Proxy server to analyze the traffic that goes between the EMO robot and the living.ai servers.
33

44
## Start the proxy
5-
`go run emoProxy.go`
5+
`go run .`
6+
7+
### Alternatively start with air
8+
`air run .`
69

710
## Docker setup
811
You can find a simplified setup using Docker Compose by SebbeJohansson here: https://github.com/SebbeJohansson/emo-proxy-docker

database.go

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
package main
2+
3+
import (
4+
"database/sql"
5+
"log"
6+
7+
_ "modernc.org/sqlite"
8+
)
9+
10+
var DB *sql.DB // Capitalized to be visible (though not strictly necessary if in same package)
11+
12+
func InitDB(path string) error {
13+
_db, err := sql.Open("sqlite", path)
14+
if err != nil {
15+
return err
16+
}
17+
DB = _db // Assign to global DB variable
18+
// Create a simple table for intercepted data
19+
query := `
20+
CREATE TABLE IF NOT EXISTS requests (
21+
id INTEGER PRIMARY KEY AUTOINCREMENT,
22+
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
23+
endpoint TEXT,
24+
payload TEXT,
25+
response TEXT
26+
);`
27+
28+
_, err = DB.Exec(query)
29+
return err
30+
}
31+
32+
func saveRequest(requestEndPoint string, payload string, response string) {
33+
log.Println("Saving request to DB...")
34+
_, err := DB.Exec("INSERT INTO requests (endpoint, payload, response) VALUES (?, ?, ?)", requestEndPoint, payload, response)
35+
if err != nil {
36+
log.Println("Failed to save to DB: ", err)
37+
}
38+
}
39+
40+
func getAllRequests() ([]map[string]interface{}, error) {
41+
rows, err := DB.Query("SELECT id, timestamp, endpoint, payload, response FROM requests")
42+
if err != nil {
43+
return nil, err
44+
}
45+
defer rows.Close()
46+
47+
var results []map[string]interface{}
48+
for rows.Next() {
49+
var id int
50+
var timestamp string
51+
var endpoint string
52+
var payload string
53+
var response string
54+
55+
err := rows.Scan(&id, &timestamp, &endpoint, &payload, &response)
56+
if err != nil {
57+
return nil, err
58+
}
59+
60+
record := map[string]interface{}{
61+
"id": id,
62+
"timestamp": timestamp,
63+
"endpoint": endpoint,
64+
"payload": payload,
65+
"response": response,
66+
}
67+
results = append(results, record)
68+
}
69+
return results, rows.Err()
70+
}

emoProxy.conf renamed to emoProxy.conf.example

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,7 @@
55
"livingio_tts_server": "eu-tts.living.ai",
66
"livingio_res_server": "res.living.ai",
77
"postFS": "/tmp/",
8-
"logFileName": "/var/log/emoProxyss.log"
8+
"logFileName": "/var/log/emoProxyss.log",
9+
"enableDatabaseAndAPI": false, # For now, default behavior is still without db and api.
10+
"sqliteLocation": "/var/data/emo_logs.db"
911
}

emoProxy.go

Lines changed: 85 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -32,40 +32,63 @@ type Configuration struct {
3232
Livingio_RES_Server string `json:"livingio_res_server"`
3333
PostFS string `json:"postFS"`
3434
LogFileName string `json:"logFileName"`
35+
EnableDatabaseAndAPI bool `json:"enableDatabaseAndAPI"`
36+
SqliteLocation string `json:"sqliteLocation"`
3537
}
3638

3739
var (
38-
conf Configuration
40+
conf Configuration
41+
useDatabaseAndAPI bool = false
3942
)
4043

4144
func main() {
45+
log.Println("Starting application...")
4246
//load config
4347
confFile := flag.String("c", "emoProxy.conf", "config file to use")
48+
Port := flag.Int("port", 8080, "http port")
4449
flag.Parse()
4550

4651
err := loadConfig(*confFile)
4752
if err != nil {
4853
log.Println("can't read conf file", *confFile, "- using default config")
4954
}
50-
55+
log.Println("config loaded")
5156
writePid()
5257

5358
// disable ssl checks
5459
http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
5560

61+
// parse flags
62+
log.Println("Starting app on port: ", *Port)
63+
5664
// redirect log
5765
logFile, err := os.OpenFile(conf.LogFileName, os.O_APPEND|os.O_RDWR|os.O_CREATE, 0644)
5866
if err != nil {
5967
log.Panic(err)
6068
}
69+
6170
defer logFile.Close()
6271
log.SetOutput(logFile)
6372
log.SetFlags(log.Lshortfile | log.LstdFlags)
6473

65-
// parse flags
66-
Port := flag.Int("port", 8081, "http port")
67-
flag.Parse()
68-
log.Println("Port: ", *Port)
74+
useDatabaseAndAPI = conf.EnableDatabaseAndAPI
75+
76+
if useDatabaseAndAPI {
77+
log.Println("Database and API enabled")
78+
79+
dbPath := conf.SqliteLocation
80+
flagDbPath := flag.String("db", "", "path to the sqlite database file")
81+
if *flagDbPath != "" {
82+
dbPath = *flagDbPath
83+
}
84+
flag.Parse()
85+
dbCreateErr := InitDB(dbPath)
86+
if dbCreateErr != nil {
87+
log.Panic(dbCreateErr)
88+
}
89+
} else {
90+
log.Println("Note: Database and API disabled")
91+
}
6992

7093
// handle time requests
7194
http.HandleFunc("/time", func(w http.ResponseWriter, r *http.Request) {
@@ -152,7 +175,40 @@ func main() {
152175
fmt.Fprint(w, body)
153176
})
154177

155-
log.Fatal(http.ListenAndServe(":"+strconv.Itoa(*Port), nil))
178+
if useDatabaseAndAPI {
179+
// proxy-api endpoints
180+
http.HandleFunc("/proxy-api/requests", func(w http.ResponseWriter, r *http.Request) {
181+
logRequest(r)
182+
w.Header().Set("Content-Type", "application/json; charset=utf-8")
183+
w.WriteHeader(http.StatusOK)
184+
185+
requests, err := getAllRequests()
186+
if err != nil {
187+
http.Error(w, fmt.Sprintf(`{"error":"%v"}`, err), http.StatusInternalServerError)
188+
return
189+
}
190+
json.NewEncoder(w).Encode(requests)
191+
})
192+
}
193+
194+
log.Fatal(http.ListenAndServe(":"+strconv.Itoa(*Port), corsMiddleware(http.DefaultServeMux)))
195+
}
196+
197+
func corsMiddleware(next http.Handler) http.Handler {
198+
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
199+
// Replace "*" with "http://localhost:3000" for better security
200+
w.Header().Set("Access-Control-Allow-Origin", "http://localhost:3000")
201+
w.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE")
202+
w.Header().Set("Access-Control-Allow-Headers", "Accept, Content-Type, Content-Length, Accept-Encoding, Authorization")
203+
204+
// Handle the preflight OPTIONS request
205+
if r.Method == "OPTIONS" {
206+
w.WriteHeader(http.StatusOK)
207+
return
208+
}
209+
210+
next.ServeHTTP(w, r)
211+
})
156212
}
157213

158214
func loadConfig(filename string) error {
@@ -164,6 +220,8 @@ func loadConfig(filename string) error {
164220
Livingio_RES_Server: "res.living.ai",
165221
PostFS: "/tmp/",
166222
LogFileName: "/var/log/emoProxy.log",
223+
EnableDatabaseAndAPI: false,
224+
SqliteLocation: "/var/data/emo_logs.db",
167225
}
168226

169227
bytes, err := os.ReadFile(filename)
@@ -228,16 +286,17 @@ func logBody(contentType string, body []byte, prefix string) {
228286

229287
func makeApiRequest(r *http.Request) string {
230288
var request *http.Request
289+
var requestBody []byte
231290
switch r.Method {
232291
case "GET":
233292
request, _ = http.NewRequest("GET", "https://"+conf.Livingio_API_Server+r.URL.RequestURI(), nil)
234293
case "POST":
235-
body, _ := io.ReadAll(r.Body)
294+
requestBody, _ := io.ReadAll(r.Body)
236295

237296
// write post request body to fs
238-
logBody(r.Header.Get("Content-Type"), body, "apiReq_")
297+
logBody(r.Header.Get("Content-Type"), requestBody, "apiReq_")
239298

240-
request, _ = http.NewRequest("POST", "https://"+conf.Livingio_API_Server+r.URL.RequestURI(), bytes.NewBuffer(body))
299+
request, _ = http.NewRequest("POST", "https://"+conf.Livingio_API_Server+r.URL.RequestURI(), bytes.NewBuffer(requestBody))
241300

242301
request.Header.Add("Content-Type", r.Header.Get("Content-Type"))
243302
request.Header.Add("Content-Length", r.Header.Get("Content-Length"))
@@ -269,6 +328,10 @@ func makeApiRequest(r *http.Request) string {
269328
log.Println("Server response: ", string(body))
270329

271330
logResponse(response)
331+
332+
if useDatabaseAndAPI {
333+
saveRequest(r.URL.RequestURI(), string(requestBody), string(body))
334+
}
272335
return string(body)
273336
}
274337

@@ -301,6 +364,10 @@ func makeTtsRequest(r *http.Request) string {
301364
// write post request body to fs
302365
logBody(response.Header.Get("Content-Type"), body, "tts_")
303366
logResponse(response)
367+
368+
if useDatabaseAndAPI {
369+
saveRequest(r.URL.RequestURI(), "", "")
370+
}
304371
return string(body)
305372
}
306373

@@ -333,6 +400,10 @@ func makeApiTtsRequest(r *http.Request) string {
333400
// write post request body to fs
334401
logBody(response.Header.Get("Content-Type"), body, "apitts_")
335402
logResponse(response)
403+
404+
if useDatabaseAndAPI {
405+
saveRequest(r.URL.RequestURI(), "", string(body))
406+
}
336407
return string(body)
337408
}
338409

@@ -369,5 +440,9 @@ func makeResRequest(r *http.Request, w http.ResponseWriter) string {
369440
}
370441

371442
logResponse(response)
443+
444+
if useDatabaseAndAPI {
445+
saveRequest(r.URL.RequestURI(), "", string(body))
446+
}
372447
return string(body)
373448
}

go.mod

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,18 @@
11
module emoProxy
22

3-
go 1.17
3+
go 1.24
4+
5+
require modernc.org/sqlite v1.42.2
6+
7+
require (
8+
github.com/dustin/go-humanize v1.0.1 // indirect
9+
github.com/google/uuid v1.6.0 // indirect
10+
github.com/mattn/go-isatty v0.0.20 // indirect
11+
github.com/ncruces/go-strftime v0.1.9 // indirect
12+
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
13+
golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b // indirect
14+
golang.org/x/sys v0.36.0 // indirect
15+
modernc.org/libc v1.66.10 // indirect
16+
modernc.org/mathutil v1.7.1 // indirect
17+
modernc.org/memory v1.11.0 // indirect
18+
)

go.sum

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
2+
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
3+
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
4+
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
5+
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
6+
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
7+
github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4=
8+
github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
9+
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
10+
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
11+
golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b h1:M2rDM6z3Fhozi9O7NWsxAkg/yqS/lQJ6PmkyIV3YP+o=
12+
golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b/go.mod h1:3//PLf8L/X+8b4vuAfHzxeRUl04Adcb341+IGKfnqS8=
13+
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
14+
golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k=
15+
golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
16+
modernc.org/libc v1.66.10 h1:yZkb3YeLx4oynyR+iUsXsybsX4Ubx7MQlSYEw4yj59A=
17+
modernc.org/libc v1.66.10/go.mod h1:8vGSEwvoUoltr4dlywvHqjtAqHBaw0j1jI7iFBTAr2I=
18+
modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU=
19+
modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg=
20+
modernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI=
21+
modernc.org/memory v1.11.0/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw=
22+
modernc.org/sqlite v1.42.2 h1:7hkZUNJvJFN2PgfUdjni9Kbvd4ef4mNLOu0B9FGxM74=
23+
modernc.org/sqlite v1.42.2/go.mod h1:+VkC6v3pLOAE0A0uVucQEcbVW0I5nHCeDaBf+DpsQT8=

0 commit comments

Comments
 (0)