Skip to content
This repository was archived by the owner on Mar 4, 2025. It is now read-only.

Commit c92f0f8

Browse files
committed
api, common. Log all API calls into our database, for stats generation
With this commit, we record basic info for each API call so we can start doing useful statistic generation for API usage. The backend database needs a new table created to hold the info: CREATE TABLE public.api_call_log ( api_call_id bigint NOT NULL, api_call_date timestamp with time zone DEFAULT now(), caller_id bigint, db_owner_id bigint, db_id bigint, api_operation text NOT NULL, api_caller_sw text ); COMMENT ON COLUMN public.api_call_log.db_owner_id IS 'This field must be nullable, as not all api calls act on a database'; COMMENT ON COLUMN public.api_call_log.db_id IS 'This field must be nullable, as not all api calls act on a database'; CREATE SEQUENCE public.api_log_log_id_seq START WITH 1 INCREMENT BY 1 NO MINVALUE NO MAXVALUE CACHE 1; ALTER SEQUENCE public.api_log_log_id_seq OWNED BY public.api_call_log.api_call_id; ALTER TABLE ONLY public.api_call_log ALTER COLUMN api_call_id SET DEFAULT nextval('public.api_log_log_id_seq'::regclass); ALTER TABLE ONLY public.api_call_log ADD CONSTRAINT api_log_users_user_id_fk FOREIGN KEY (caller_id) REFERENCES public.users(user_id);
1 parent 5c78e52 commit c92f0f8

File tree

8 files changed

+246
-51
lines changed

8 files changed

+246
-51
lines changed

api/handlers.go

Lines changed: 83 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"net/http"
1010
"net/url"
1111
"os"
12+
"path/filepath"
1213
"sort"
1314

1415
sqlite "github.com/gwenn/gosqlite"
@@ -27,12 +28,15 @@ import (
2728
// * "dbname" is the name of the database
2829
func branchesHandler(w http.ResponseWriter, r *http.Request) {
2930
// Do auth check, grab request info
30-
_, dbOwner, dbName, _, httpStatus, err := collectInfo(w, r)
31+
loggedInUser, dbOwner, dbName, _, httpStatus, err := collectInfo(w, r)
3132
if err != nil {
3233
jsonErr(w, err.Error(), httpStatus)
3334
return
3435
}
3536

37+
// Record the api call in our backend database
38+
com.ApiCallLog(loggedInUser, dbOwner, dbName, "branches", r.Header.Get("User-Agent"))
39+
3640
// If the database is a live database, we return an error message
3741
isLive, _, err := com.CheckDBLive(dbOwner, dbName)
3842
if err != nil {
@@ -102,6 +106,9 @@ func columnsHandler(w http.ResponseWriter, r *http.Request) {
102106
return
103107
}
104108

109+
// Record the api call in our backend database
110+
com.ApiCallLog(loggedInUser, dbOwner, dbName, "columns", r.Header.Get("User-Agent"))
111+
105112
// Extract the table name
106113
table, err := com.GetFormTable(r, false)
107114
if err != nil {
@@ -221,12 +228,15 @@ func columnsHandler(w http.ResponseWriter, r *http.Request) {
221228
// * "dbname" is the name of the database
222229
func commitsHandler(w http.ResponseWriter, r *http.Request) {
223230
// Do auth check, grab request info
224-
_, dbOwner, dbName, _, httpStatus, err := collectInfo(w, r)
231+
loggedInUser, dbOwner, dbName, _, httpStatus, err := collectInfo(w, r)
225232
if err != nil {
226233
jsonErr(w, err.Error(), httpStatus)
227234
return
228235
}
229236

237+
// Record the api call in our backend database
238+
com.ApiCallLog(loggedInUser, dbOwner, dbName, "commits", r.Header.Get("User-Agent"))
239+
230240
// If the database is a live database, we return an error message
231241
isLive, _, err := com.CheckDBLive(dbOwner, dbName)
232242
if err != nil {
@@ -279,6 +289,13 @@ func databasesHandler(w http.ResponseWriter, r *http.Request) {
279289
return
280290
}
281291

292+
// Record the api call in our backend database
293+
operation := "databases"
294+
if live {
295+
operation = "LIVE databases"
296+
}
297+
com.ApiCallLog(loggedInUser, "", "", operation, r.Header.Get("User-Agent"))
298+
282299
// Retrieve the list of databases in the user account
283300
var databases []com.DBInfo
284301
if !live {
@@ -336,6 +353,9 @@ func deleteHandler(w http.ResponseWriter, r *http.Request) {
336353
}
337354
dbOwner := loggedInUser
338355

356+
// Record the api call in our backend database
357+
com.ApiCallLog(loggedInUser, dbOwner, dbName, "delete", r.Header.Get("User-Agent"))
358+
339359
// Check if the database exists
340360
exists, err := com.CheckDBPermissions(loggedInUser, dbOwner, dbName, false)
341361
if err != nil {
@@ -517,6 +537,10 @@ func diffHandler(w http.ResponseWriter, r *http.Request) {
517537
return
518538
}
519539

540+
// Record the api call in our backend database
541+
// Note - Lets not bother adding additional api logging fields just for the diff function at this stage
542+
com.ApiCallLog(loggedInUser, dbOwnerA, dbNameA, "diff", r.Header.Get("User-Agent"))
543+
520544
// Check permissions of the first database
521545
var allowed bool
522546
allowed, err = com.CheckDBPermissions(loggedInUser, dbOwnerA, dbNameA, false)
@@ -593,6 +617,9 @@ func downloadHandler(w http.ResponseWriter, r *http.Request) {
593617
return
594618
}
595619

620+
// Record the api call in our backend database
621+
com.ApiCallLog(loggedInUser, dbOwner, dbName, "download", r.Header.Get("User-Agent"))
622+
596623
// Return the requested database to the user
597624
_, err = com.DownloadDatabase(w, r, dbOwner, dbName, commitID, loggedInUser, "api")
598625
if err != nil {
@@ -630,6 +657,9 @@ func executeHandler(w http.ResponseWriter, r *http.Request) {
630657
return
631658
}
632659

660+
// Record the api call in our backend database
661+
com.ApiCallLog(loggedInUser, dbOwner, dbName, "execute", r.Header.Get("User-Agent"))
662+
633663
// Grab the incoming SQLite query
634664
rawInput := r.FormValue("sql")
635665
var sql string
@@ -709,6 +739,9 @@ func indexesHandler(w http.ResponseWriter, r *http.Request) {
709739
return
710740
}
711741

742+
// Record the api call in our backend database
743+
com.ApiCallLog(loggedInUser, dbOwner, dbName, "indexes", r.Header.Get("User-Agent"))
744+
712745
// Check if the database is a live database, and get the node/queue to send the request to
713746
isLive, liveNode, err := com.CheckDBLive(dbOwner, dbName)
714747
if err != nil {
@@ -826,12 +859,15 @@ func indexesHandler(w http.ResponseWriter, r *http.Request) {
826859
// * "dbname" is the name of the database
827860
func metadataHandler(w http.ResponseWriter, r *http.Request) {
828861
// Do auth check, grab request info
829-
_, dbOwner, dbName, _, httpStatus, err := collectInfo(w, r)
862+
loggedInUser, dbOwner, dbName, _, httpStatus, err := collectInfo(w, r)
830863
if err != nil {
831864
jsonErr(w, err.Error(), httpStatus)
832865
return
833866
}
834867

868+
// Record the api call in our backend database
869+
com.ApiCallLog(loggedInUser, dbOwner, dbName, "metadata", r.Header.Get("User-Agent"))
870+
835871
// If the database is a live database, we return an error message
836872
isLive, _, err := com.CheckDBLive(dbOwner, dbName)
837873
if err != nil {
@@ -886,6 +922,9 @@ func queryHandler(w http.ResponseWriter, r *http.Request) {
886922
return
887923
}
888924

925+
// Record the api call in our backend database
926+
com.ApiCallLog(loggedInUser, dbOwner, dbName, "query", r.Header.Get("User-Agent"))
927+
889928
// Grab the incoming SQLite query
890929
rawInput := r.FormValue("sql")
891930
query, err := com.CheckUnicode(rawInput)
@@ -958,12 +997,15 @@ func queryHandler(w http.ResponseWriter, r *http.Request) {
958997
// * "dbname" is the name of the database
959998
func releasesHandler(w http.ResponseWriter, r *http.Request) {
960999
// Do auth check, grab request info
961-
_, dbOwner, dbName, _, httpStatus, err := collectInfo(w, r)
1000+
loggedInUser, dbOwner, dbName, _, httpStatus, err := collectInfo(w, r)
9621001
if err != nil {
9631002
jsonErr(w, err.Error(), httpStatus)
9641003
return
9651004
}
9661005

1006+
// Record the api call in our backend database
1007+
com.ApiCallLog(loggedInUser, dbOwner, dbName, "releases", r.Header.Get("User-Agent"))
1008+
9671009
// If the database is a live database, we return an error message
9681010
isLive, _, err := com.CheckDBLive(dbOwner, dbName)
9691011
if err != nil {
@@ -1033,6 +1075,9 @@ func tablesHandler(w http.ResponseWriter, r *http.Request) {
10331075
return
10341076
}
10351077

1078+
// Record the api call in our backend database
1079+
com.ApiCallLog(loggedInUser, dbOwner, dbName, "tables", r.Header.Get("User-Agent"))
1080+
10361081
// Check if the database is a live database, and get the node/queue to send the request to
10371082
isLive, liveNode, err := com.CheckDBLive(dbOwner, dbName)
10381083
if err != nil {
@@ -1108,12 +1153,15 @@ func tablesHandler(w http.ResponseWriter, r *http.Request) {
11081153
// * "dbname" is the name of the database
11091154
func tagsHandler(w http.ResponseWriter, r *http.Request) {
11101155
// Do auth check, grab request info
1111-
_, dbOwner, dbName, _, httpStatus, err := collectInfo(w, r)
1156+
loggedInUser, dbOwner, dbName, _, httpStatus, err := collectInfo(w, r)
11121157
if err != nil {
11131158
jsonErr(w, err.Error(), httpStatus)
11141159
return
11151160
}
11161161

1162+
// Record the api call in our backend database
1163+
com.ApiCallLog(loggedInUser, dbOwner, dbName, "tags", r.Header.Get("User-Agent"))
1164+
11171165
// If the database is a live database, we return an error message
11181166
isLive, _, err := com.CheckDBLive(dbOwner, dbName)
11191167
if err != nil {
@@ -1314,18 +1362,39 @@ func uploadHandler(w http.ResponseWriter, r *http.Request) {
13141362
com.SanitiseLogString(dbOwner), com.SanitiseLogString(dbName), numBytes)
13151363

13161364
// Send a request to the AMQP backend to set up the database there, ready for querying
1317-
err = com.LiveCreateDB(com.AmqpChan, dbOwner, dbName, objectID, com.SetToPrivate)
1365+
liveNode, err := com.LiveCreateDB(com.AmqpChan, dbOwner, dbName, objectID)
13181366
if err != nil {
13191367
log.Println(err)
13201368
jsonErr(w, err.Error(), http.StatusInternalServerError)
13211369
return
13221370
}
13231371

1372+
// Update PG, so it has a record of this database existing and knows the node/queue name for querying it
1373+
err = com.LiveAddDatabasePG(dbOwner, dbName, objectID, liveNode, com.SetToPrivate)
1374+
if err != nil {
1375+
jsonErr(w, err.Error(), http.StatusInternalServerError)
1376+
return
1377+
}
1378+
1379+
// Enable the watch flag for the uploader for this database
1380+
err = com.ToggleDBWatch(dbOwner, dbOwner, dbName)
1381+
if err != nil {
1382+
jsonErr(w, err.Error(), http.StatusInternalServerError)
1383+
return
1384+
}
1385+
13241386
// Upload was successful, so we construct a fake commit ID then return a success message to the user
13251387
x = make(map[string]string)
13261388
x["commit_id"] = ""
1327-
x["url"] = fmt.Sprintf("/%s", dbOwner)
1389+
x["url"] = server + filepath.Join("/", dbOwner, dbName)
1390+
}
1391+
1392+
// Record the api call in our backend database
1393+
operation := "upload"
1394+
if live {
1395+
operation = "LIVE upload"
13281396
}
1397+
com.ApiCallLog(loggedInUser, loggedInUser, dbName, operation, r.Header.Get("User-Agent"))
13291398

13301399
// Construct the response message
13311400
var ok bool
@@ -1366,6 +1435,9 @@ func viewsHandler(w http.ResponseWriter, r *http.Request) {
13661435
return
13671436
}
13681437

1438+
// Record the api call in our backend database
1439+
com.ApiCallLog(loggedInUser, loggedInUser, dbName, "views", r.Header.Get("User-Agent"))
1440+
13691441
// Check if the database is a live database, and get the node/queue to send the request to
13701442
isLive, liveNode, err := com.CheckDBLive(dbOwner, dbName)
13711443
if err != nil {
@@ -1441,12 +1513,15 @@ func viewsHandler(w http.ResponseWriter, r *http.Request) {
14411513
// * "dbname" is the name of the database being queried
14421514
func webpageHandler(w http.ResponseWriter, r *http.Request) {
14431515
// Authenticate user and collect requested database details
1444-
_, dbOwner, dbName, _, httpStatus, err := collectInfo(w, r)
1516+
loggedInUser, dbOwner, dbName, _, httpStatus, err := collectInfo(w, r)
14451517
if err != nil {
14461518
jsonErr(w, err.Error(), httpStatus)
14471519
return
14481520
}
14491521

1522+
// Record the api call in our backend database
1523+
com.ApiCallLog(loggedInUser, dbOwner, dbName, "webpage", r.Header.Get("User-Agent"))
1524+
14501525
// Return the database webUI URL to the user
14511526
var z com.WebpageResponseContainer
14521527
z.WebPage = "https://" + com.Conf.Web.ServerName + "/" + dbOwner + "/" + dbName

api/main.go

Lines changed: 24 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,6 @@ package main
33
// TODO: API functions that still need updating for Live databases
44
// * diff - already updated to just return an error for live databases. needs testing though
55

6-
// FIXME: Update the documented Upload() function return values on the API doc page. Currently it talks about
7-
// returning the commit ID for the upload. We'll probably return that field with a blank value for live
8-
// databases though. TBD.
9-
106
// FIXME: After the API and webui pieces are done, figure out how the DB4S end
117
// point and dio should be updated to use live databases too
128

@@ -113,29 +109,29 @@ func main() {
113109
}
114110

115111
// Our pages
116-
http.Handle("/", gz.GzipHandler(handleWrapper(rootHandler)))
117-
http.Handle("/changelog", gz.GzipHandler(handleWrapper(changeLogHandler)))
118-
http.Handle("/changelog.html", gz.GzipHandler(handleWrapper(changeLogHandler)))
119-
http.Handle("/v1/branches", gz.GzipHandler(handleWrapper(branchesHandler)))
120-
http.Handle("/v1/columns", gz.GzipHandler(handleWrapper(columnsHandler)))
121-
http.Handle("/v1/commits", gz.GzipHandler(handleWrapper(commitsHandler)))
122-
http.Handle("/v1/databases", gz.GzipHandler(handleWrapper(databasesHandler)))
123-
http.Handle("/v1/delete", gz.GzipHandler(handleWrapper(deleteHandler)))
124-
http.Handle("/v1/diff", gz.GzipHandler(handleWrapper(diffHandler)))
125-
http.Handle("/v1/download", gz.GzipHandler(handleWrapper(downloadHandler)))
126-
http.Handle("/v1/execute", gz.GzipHandler(handleWrapper(executeHandler)))
127-
http.Handle("/v1/indexes", gz.GzipHandler(handleWrapper(indexesHandler)))
128-
http.Handle("/v1/metadata", gz.GzipHandler(handleWrapper(metadataHandler)))
129-
http.Handle("/v1/query", gz.GzipHandler(handleWrapper(queryHandler)))
130-
http.Handle("/v1/releases", gz.GzipHandler(handleWrapper(releasesHandler)))
131-
http.Handle("/v1/tables", gz.GzipHandler(handleWrapper(tablesHandler)))
132-
http.Handle("/v1/tags", gz.GzipHandler(handleWrapper(tagsHandler)))
133-
http.Handle("/v1/upload", gz.GzipHandler(handleWrapper(uploadHandler)))
134-
http.Handle("/v1/views", gz.GzipHandler(handleWrapper(viewsHandler)))
135-
http.Handle("/v1/webpage", gz.GzipHandler(handleWrapper(webpageHandler)))
112+
http.Handle("/", gz.GzipHandler(corsWrapper(rootHandler)))
113+
http.Handle("/changelog", gz.GzipHandler(corsWrapper(changeLogHandler)))
114+
http.Handle("/changelog.html", gz.GzipHandler(corsWrapper(changeLogHandler)))
115+
http.Handle("/v1/branches", gz.GzipHandler(corsWrapper(branchesHandler)))
116+
http.Handle("/v1/columns", gz.GzipHandler(corsWrapper(columnsHandler)))
117+
http.Handle("/v1/commits", gz.GzipHandler(corsWrapper(commitsHandler)))
118+
http.Handle("/v1/databases", gz.GzipHandler(corsWrapper(databasesHandler)))
119+
http.Handle("/v1/delete", gz.GzipHandler(corsWrapper(deleteHandler)))
120+
http.Handle("/v1/diff", gz.GzipHandler(corsWrapper(diffHandler)))
121+
http.Handle("/v1/download", gz.GzipHandler(corsWrapper(downloadHandler)))
122+
http.Handle("/v1/execute", gz.GzipHandler(corsWrapper(executeHandler)))
123+
http.Handle("/v1/indexes", gz.GzipHandler(corsWrapper(indexesHandler)))
124+
http.Handle("/v1/metadata", gz.GzipHandler(corsWrapper(metadataHandler)))
125+
http.Handle("/v1/query", gz.GzipHandler(corsWrapper(queryHandler)))
126+
http.Handle("/v1/releases", gz.GzipHandler(corsWrapper(releasesHandler)))
127+
http.Handle("/v1/tables", gz.GzipHandler(corsWrapper(tablesHandler)))
128+
http.Handle("/v1/tags", gz.GzipHandler(corsWrapper(tagsHandler)))
129+
http.Handle("/v1/upload", gz.GzipHandler(corsWrapper(uploadHandler)))
130+
http.Handle("/v1/views", gz.GzipHandler(corsWrapper(viewsHandler)))
131+
http.Handle("/v1/webpage", gz.GzipHandler(corsWrapper(webpageHandler)))
136132

137133
// favicon.ico
138-
http.Handle("/favicon.ico", gz.GzipHandler(handleWrapper(func(w http.ResponseWriter, r *http.Request) {
134+
http.Handle("/favicon.ico", gz.GzipHandler(corsWrapper(func(w http.ResponseWriter, r *http.Request) {
139135
logReq(r, "-")
140136
http.ServeFile(w, r, filepath.Join(com.Conf.Web.BaseDir, "webui", "favicon.ico"))
141137
})))
@@ -329,9 +325,8 @@ func extractUserFromClientCert(w http.ResponseWriter, r *http.Request) (userAcc
329325
return
330326
}
331327

332-
// handleWrapper does nothing useful except interface between types
333-
// TODO: Get rid of this, as it shouldn't be needed
334-
func handleWrapper(fn http.HandlerFunc) http.HandlerFunc {
328+
// corsWrapper sets a general allow for all our api calls
329+
func corsWrapper(fn http.HandlerFunc) http.HandlerFunc {
335330
return func(w http.ResponseWriter, r *http.Request) {
336331
// Enable CORS (https://enable-cors.org)
337332
w.Header().Set("Access-Control-Allow-Origin", "*")
@@ -361,6 +356,5 @@ func jsonErr(w http.ResponseWriter, msg string, statusCode int) {
361356
// logReq writes an entry for the incoming request to the request log
362357
func logReq(r *http.Request, loggedInUser string) {
363358
fmt.Fprintf(reqLog, "%v - %s [%s] \"%s %s %s\" \"-\" \"-\" \"%s\" \"%s\"\n", r.RemoteAddr,
364-
loggedInUser, time.Now().Format(time.RFC3339Nano), r.Method, r.URL, r.Proto,
365-
r.Referer(), r.Header.Get("User-Agent"))
359+
loggedInUser, time.Now().Format(time.RFC3339Nano), r.Method, r.URL, r.Proto, r.Referer(), r.Header.Get("User-Agent"))
366360
}

common/cypress.go

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,13 +85,29 @@ func CypressSeed(w http.ResponseWriter, r *http.Request) {
8585
}
8686

8787
// Send the live database file to our AMQP backend for setup
88-
err = LiveCreateDB(AmqpChan, "default", "Join Testing with index.sqlite", objectID, SetToPrivate)
88+
dbOwner := "default"
89+
dbName := "Join Testing with index.sqlite"
90+
liveNode, err := LiveCreateDB(AmqpChan, dbOwner, dbName, objectID)
8991
if err != nil {
9092
log.Println(err)
9193
http.Error(w, err.Error(), http.StatusInternalServerError)
9294
return
9395
}
9496

97+
// Update PG, so it has a record of this database existing and knows the node/queue name for querying it
98+
err = LiveAddDatabasePG(dbOwner, dbName, objectID, liveNode, SetToPrivate)
99+
if err != nil {
100+
http.Error(w, err.Error(), http.StatusInternalServerError)
101+
return
102+
}
103+
104+
// Enable the watch flag for the uploader for this database
105+
err = ToggleDBWatch(dbOwner, dbOwner, dbName)
106+
if err != nil {
107+
http.Error(w, err.Error(), http.StatusInternalServerError)
108+
return
109+
}
110+
95111
// *** Add a test LIVE SQLite database (end) ***
96112

97113
// Add some test users

common/live.go

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ func LiveColumns(liveNode, loggedInUser, dbOwner, dbName, table string) (columns
133133
}
134134

135135
// LiveCreateDB requests the AMQP backend create a new live SQLite database
136-
func LiveCreateDB(channel *amqp.Channel, dbOwner, dbName, objectID string, accessType SetAccessType) (err error) {
136+
func LiveCreateDB(channel *amqp.Channel, dbOwner, dbName, objectID string) (liveNode string, err error) {
137137
// Send the database setup request to our AMQP backend
138138
var rawResponse []byte
139139
rawResponse, err = MQRequest(channel, "create_queue", "createdb", "", dbOwner, dbName, objectID)
@@ -161,15 +161,7 @@ func LiveCreateDB(channel *amqp.Channel, dbOwner, dbName, objectID string, acces
161161
dbOwner, dbName))
162162
return
163163
}
164-
165-
// Update PG, so it has a record of this database existing and knows the node/queue name for querying it
166-
err = LiveAddDatabasePG(dbOwner, dbName, objectID, resp.Node, accessType)
167-
if err != nil {
168-
return
169-
}
170-
171-
// Enable the watch flag for the uploader for this database
172-
err = ToggleDBWatch(dbOwner, dbOwner, dbName)
164+
liveNode = resp.Node
173165
return
174166
}
175167

0 commit comments

Comments
 (0)