refactor(errors & responses): remove repetitive code and make all responses in json Made all responses be in JSON, this won't make much difference now but it will be easier to write frontend logic later. Also probably reduced total lines of code.
Brijesh ops@brijesh.dev
Mon, 08 Jul 2024 16:20:55 +0530
7 files changed,
90 insertions(+),
147 deletions(-)
A
Makefile
@@ -0,0 +1,13 @@
+.PHONY: schema-generate + +db-schema: + if [ -f schema/schema.sql ]; then rm schema/schema.sql; fi; + sqlite3 watchman.db .schema > schema/schema.sql; + +run-server: + go mod tidy + go run main.go + +run-frontend: + cd frontend && git pull origin master + bun run dev
M
internal/logs.go
→
internal/logs.go
@@ -16,37 +16,26 @@
var logs []schema.Log decoder := json.NewDecoder(r.Body) err := decoder.Decode(&logs) - if err != nil { - w.WriteHeader(http.StatusBadRequest) - fmt.Fprintf(w, "Error decoding log data: %v", err) - return - } + utils.HandleError(w, r, http.StatusBadRequest, "Error decoding JSON: ", err) + + fmt.Println("Logs: ", logs) + stmt, err := db.Prepare("INSERT INTO Logs (Time, Level, Message, Subject, UserID, ProjectID) VALUES (?, ?, ?, ?, ?, ?)") - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - fmt.Fprintf(w, "Error preparing SQL statement: %v", err) - return - } + utils.HandleError(w, r, http.StatusInternalServerError, "Error preparing statement: ", err) defer stmt.Close() + for _, log := range logs { log.Time = int32(time.Now().Unix()) _, err = stmt.Exec(log.Time, log.Level, log.Message, log.Subject, log.UserID, log.ProjectID) - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - fmt.Fprintf(w, "Error inserting log into database: %v", err) - return - } + utils.HandleError(w, r, http.StatusInternalServerError, "Error inserting into database: ", err) } + response := schema.Response_Type{ Status: "OK", Message: "Logs created successfully", RequestID: r.Context().Value(schema.RequestIDKey{}).(string), } - w.Header().Set("Content-Type", "application/json") - err = json.NewEncoder(w).Encode(response) - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - } + utils.SendResponse(w, r, response) } func GetLogs(w http.ResponseWriter, r *http.Request, db *sql.DB) {@@ -83,22 +72,14 @@ query = query[:len(query)-7]
} rows, err := db.Query(query) - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - fmt.Fprintf(w, "Error querying database: %v", err) - return - } + utils.HandleError(w, r, http.StatusInternalServerError, "Error querying database: ", err) defer rows.Close() var logs []schema.Log for rows.Next() { var log schema.Log err = rows.Scan(&log.Time, &log.Level, &log.Message, &log.Subject, &log.UserID, &log.ProjectID) - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - fmt.Fprintf(w, "Error scanning row: %v", err) - return - } + utils.HandleError(w, r, http.StatusInternalServerError, "Error scanning row: ", err) logs = append(logs, log) }@@ -115,7 +96,6 @@ w.WriteHeader(http.StatusInternalServerError)
} } -// DeleteLogs function similar to above GetLogs that takes in the same parameters and deletes the logs from the database func DeleteLogs(w http.ResponseWriter, r *http.Request, db *sql.DB) { utils.HandleMethodNotAllowed(w, r, http.MethodDelete)@@ -142,28 +122,23 @@ if level != "" {
query += "Level = '" + level + "' AND " } - query = query[:len(query)-5] - stmt, err := db.Prepare(query) - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - fmt.Fprintf(w, "Error preparing SQL statement: %v", err) - return + if projectID != "" || userID != "" || startTime != "" || endTime != "" || level != "" { + query = query[:len(query)-5] + } else { + query = query[:len(query)-7] } + + stmt, err := db.Prepare(query) + utils.HandleError(w, r, http.StatusInternalServerError, "Error preparing statement: ", err) defer stmt.Close() + _, err = stmt.Exec() - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - fmt.Fprintf(w, "Error deleting logs from database: %v", err) - return - } + utils.HandleError(w, r, http.StatusInternalServerError, "Error executing statement: ", err) + response := schema.Response_Type{ Status: "OK", Message: "Logs deleted successfully", RequestID: r.Context().Value(schema.RequestIDKey{}).(string), } - w.Header().Set("Content-Type", "application/json") - err = json.NewEncoder(w).Encode(response) - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - } + utils.SendResponse(w, r, response) }
M
internal/projects.go
→
internal/projects.go
@@ -3,7 +3,6 @@
import ( "database/sql" "encoding/json" - "fmt" "net/http" "watchman/schema" "watchman/utils"@@ -15,42 +14,25 @@
var project schema.Project decoder := json.NewDecoder(r.Body) err := decoder.Decode(&project) - if err != nil { - w.WriteHeader(http.StatusBadRequest) - fmt.Fprintf(w, "Error decoding project data: %v", err) - return - } + utils.HandleError(w, r, http.StatusBadRequest, "Error decoding JSON: ", err) if project.ID == "" { project.ID = utils.Generate_UUID() } stmt, err := db.Prepare("INSERT INTO Projects (ID, Name) VALUES (?, ?)") - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - fmt.Fprintf(w, "Error preparing SQL statement: %v", err) - return - } + utils.HandleError(w, r, http.StatusInternalServerError, "Error preparing statement: ", err) defer stmt.Close() _, err = stmt.Exec(project.ID, project.Name) - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - fmt.Fprintf(w, "Error inserting project into database: %v", err) - return - } + utils.HandleError(w, r, http.StatusInternalServerError, "Error executing statement: ", err) response := schema.Response_Type{ Status: "OK", Message: "Project created successfully", RequestID: r.Context().Value(schema.RequestIDKey{}).(string), } - - w.Header().Set("Content-Type", "application/json") - err = json.NewEncoder(w).Encode(response) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - } + utils.SendResponse(w, r, response) } func GetProjectByID(w http.ResponseWriter, r *http.Request, db *sql.DB, projectID string) {@@ -59,43 +41,29 @@
row := db.QueryRow("SELECT * FROM Projects WHERE ID = ?", projectID) var projectByID schema.Project err := row.Scan(&projectByID.ID, &projectByID.Name) - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - fmt.Fprintf(w, "Error querying database: %v", err) - return - } + utils.HandleError(w, r, http.StatusInternalServerError, "Error querying database: ", err) + response := schema.Response_Type{ Status: "OK", Message: "Project retrieved successfully", RequestID: r.Context().Value(schema.RequestIDKey{}).(string), Data: projectByID, } - w.Header().Set("Content-Type", "application/json") - err = json.NewEncoder(w).Encode(response) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - } + utils.SendResponse(w, r, response) } func ListAllProjects(w http.ResponseWriter, r *http.Request, db *sql.DB) { utils.HandleMethodNotAllowed(w, r, http.MethodGet) rows, err := db.Query("SELECT * FROM Projects") - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - fmt.Fprintf(w, "Error querying database: %v", err) - return - } + utils.HandleError(w, r, http.StatusInternalServerError, "Error querying database: ", err) defer rows.Close() + var projects []schema.Project for rows.Next() { var project schema.Project err := rows.Scan(&project.ID, &project.Name) - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - fmt.Fprintf(w, "Error scanning row: %v", err) - return - } + utils.HandleError(w, r, http.StatusInternalServerError, "Error scanning row: ", err) projects = append(projects, project) } response := schema.Response_Type{@@ -104,11 +72,7 @@ Message: "Projects retrieved successfully",
RequestID: r.Context().Value(schema.RequestIDKey{}).(string), Data: projects, } - w.Header().Set("Content-Type", "application/json") - err = json.NewEncoder(w).Encode(response) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - } + utils.SendResponse(w, r, response) } func UpdateProjectByID(w http.ResponseWriter, r *http.Request, db *sql.DB, projectID string) {@@ -117,66 +81,37 @@
var project schema.Project decoder := json.NewDecoder(r.Body) err := decoder.Decode(&project) - if err != nil { - w.WriteHeader(http.StatusBadRequest) - fmt.Fprintf(w, "Error decoding project data: %v", err) - return - } + utils.HandleError(w, r, http.StatusBadRequest, "Error decoding JSON: ", err) stmt, err := db.Prepare("UPDATE Projects SET Name = ?, ID = ? WHERE ID = ?") - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - fmt.Fprintf(w, "Error preparing SQL statement: %v", err) - return - } + utils.HandleError(w, r, http.StatusInternalServerError, "Error preparing statement: ", err) defer stmt.Close() _, err = stmt.Exec(project.Name, project.ID, projectID) - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - fmt.Fprintf(w, "Error updating project in database: %v", err) - return - } + utils.HandleError(w, r, http.StatusInternalServerError, "Error executing statement: ", err) response := schema.Response_Type{ Status: "OK", Message: "Project updated successfully", RequestID: r.Context().Value(schema.RequestIDKey{}).(string), } - - w.Header().Set("Content-Type", "application/json") - err = json.NewEncoder(w).Encode(response) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - } + utils.SendResponse(w, r, response) } func DeleteProjectByID(w http.ResponseWriter, r *http.Request, db *sql.DB, projectID string) { utils.HandleMethodNotAllowed(w, r, http.MethodDelete) stmt, err := db.Prepare("DELETE FROM Projects WHERE ID = ?") - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - fmt.Fprintf(w, "Error preparing SQL statement: %v", err) - return - } + utils.HandleError(w, r, http.StatusInternalServerError, "Error preparing statement: ", err) defer stmt.Close() _, err = stmt.Exec(projectID) - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - fmt.Fprintf(w, "Error deleting project from database: %v", err) - return - } + utils.HandleError(w, r, http.StatusInternalServerError, "Error executing statement: ", err) response := schema.Response_Type{ Status: "OK", Message: "Project deleted successfully", RequestID: r.Context().Value(schema.RequestIDKey{}).(string), } - w.Header().Set("Content-Type", "application/json") - err = json.NewEncoder(w).Encode(response) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - } + utils.SendResponse(w, r, response) }
M
main.go
→
main.go
@@ -29,6 +29,8 @@ if !utils.Verify_ENV_Exists(env_var) {
log.Fatal(RED + "Error: " + env_var + " not found in .env file" + RESET_COLOUR) } } + + go middleware.CleanupVisitors() } func main() {@@ -100,7 +102,7 @@ utils.Method_Not_Allowed_Handler(w, r)
} }) - fmt.Println("Starting server on " + strconv.Itoa(config.Port)) + fmt.Println("Starting server on port " + strconv.Itoa(config.Port)) log.Fatal(http.ListenAndServe(":"+strconv.Itoa(config.Port), middleware.CorsMiddleware( middleware.RequestIDMiddleware(
M
middleware/rate_limiting.go
→
middleware/rate_limiting.go
@@ -25,11 +25,6 @@ visitors = make(map[string]*visitor)
mu sync.Mutex ) -// Run cleanup function in a background go routine -func init() { - go cleanupVisitors() -} - // Get the visitor from the visitors map based on the IP address func getVisitor(ip string) *rate.Limiter { mu.Lock()@@ -49,7 +44,7 @@ return v.limiter
} // Delete the visitor if it was last seen over 3 minutes ago -func cleanupVisitors() { +func CleanupVisitors() { for { time.Sleep(time.Minute)
M
utils/errors.go
→
utils/errors.go
@@ -1,21 +1,22 @@
package utils import ( - "encoding/json" "net/http" "watchman/schema" ) + +func HandleError(w http.ResponseWriter, r *http.Request, statusCode int, message string, err error) { + w.WriteHeader(statusCode) + response := schema.Response_Type{ + Status: "ERROR", + Message: message + err.Error(), + RequestID: r.Context().Value(schema.RequestIDKey{}).(string), + } + SendResponse(w, r, response) +} func HandleMethodNotAllowed(w http.ResponseWriter, r *http.Request, method string) { if r.Method != method { - response := schema.Response_Type{ - Status: "ERROR", - Message: "Method " + method + " not allowed", - RequestID: r.Context().Value(schema.RequestIDKey{}).(string), - } - - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(http.StatusMethodNotAllowed) - json.NewEncoder(w).Encode(response) + HandleError(w, r, http.StatusMethodNotAllowed, "Method "+method+" not allowed", nil) } }
A
utils/response.go
@@ -0,0 +1,22 @@
+package utils + +import ( + "encoding/json" + "net/http" + "watchman/schema" +) + +func SendResponse(w http.ResponseWriter, r *http.Request, response schema.Response_Type) { + w.Header().Set("Content-Type", "application/json") + + err := json.NewEncoder(w).Encode(response) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + + json.NewEncoder(w).Encode(schema.Response_Type{ + Status: "ERROR", + Message: "Error encoding response", + RequestID: r.Context().Value(schema.RequestIDKey{}).(string), + }) + } +}