From 858174b9d268fbe9e3a03288ddd25113174a28c8 Mon Sep 17 00:00:00 2001 From: MrLetsplay2003 Date: Thu, 17 Aug 2023 20:32:43 +0000 Subject: [PATCH] initial commit --- .devcontainer/devcontainer.json | 22 +++++ .gitignore | 2 + go.mod | 5 ++ go.sum | 2 + main.go | 140 ++++++++++++++++++++++++++++++++ 5 files changed, 171 insertions(+) create mode 100644 .devcontainer/devcontainer.json create mode 100644 .gitignore create mode 100644 go.mod create mode 100644 go.sum create mode 100644 main.go diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..34962b0 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,22 @@ +// For format details, see https://aka.ms/devcontainer.json. For config options, see the +// README at: https://github.com/devcontainers/templates/tree/main/src/go +{ + "name": "Go", + // Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile + "image": "mcr.microsoft.com/devcontainers/go:0-1-bullseye" + + // Features to add to the dev container. More info: https://containers.dev/features. + // "features": {}, + + // Use 'forwardPorts' to make a list of ports inside the container available locally. + // "forwardPorts": [], + + // Use 'postCreateCommand' to run commands after the container is created. + // "postCreateCommand": "go version", + + // Configure tool-specific properties. + // "customizations": {}, + + // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root. + // "remoteUser": "root" +} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ef7c144 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +notes.sqlite +NoteServer diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..d526731 --- /dev/null +++ b/go.mod @@ -0,0 +1,5 @@ +module git.cringe-studios.com/NoteServer + +go 1.20 + +require github.com/mattn/go-sqlite3 v1.14.17 diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..04784a2 --- /dev/null +++ b/go.sum @@ -0,0 +1,2 @@ +github.com/mattn/go-sqlite3 v1.14.17 h1:mCRHCLDUBXgpKAqIKsaAaAsrAlbkeomtRFKXh2L6YIM= +github.com/mattn/go-sqlite3 v1.14.17/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= diff --git a/main.go b/main.go new file mode 100644 index 0000000..f60c155 --- /dev/null +++ b/main.go @@ -0,0 +1,140 @@ +package main + +import ( + "database/sql" + "encoding/json" + "io" + "log" + "net/http" + + _ "github.com/mattn/go-sqlite3" +) + +var db *sql.DB + +type Note struct { + Id int64 `json:"id"` + Content string `json:"content"` +} + +func httpError(err error, statusCode int, message string, w http.ResponseWriter) bool { + if err != nil { + w.WriteHeader(statusCode) + io.WriteString(w, message) + return true + } + + return false +} + +func getNotes(w http.ResponseWriter, req *http.Request) { + res, err := db.Query(`SELECT Id, Content FROM Notes`) + if httpError(err, http.StatusInternalServerError, "Failed to load notes", w) { + return + } + + defer res.Close() + + var notes []Note = []Note{} + + for res.Next() { + if httpError(res.Err(), http.StatusInternalServerError, "Failed to load notes", w) { + return + } + + var note Note + res.Scan(¬e.Id, ¬e.Content) + notes = append(notes, note) + } + + json, err := json.Marshal(notes) + if httpError(err, http.StatusInternalServerError, "Failed to create JSON", w) { + return + } + + w.Write(json) +} + +func postNotes(w http.ResponseWriter, req *http.Request) { + data, err := io.ReadAll(req.Body) + if httpError(err, http.StatusBadRequest, "Failed to read data", w) { + return + } + + var note Note + err = json.Unmarshal(data, ¬e) + if httpError(err, http.StatusBadRequest, "Invalid request", w) { + return + } + + if len(note.Content) == 0 { + w.WriteHeader(http.StatusBadRequest) + io.WriteString(w, "Missing note content") + return + } + + stmt, err := db.Prepare(`INSERT INTO Notes(Content) VALUES(?) RETURNING Id`) + if httpError(err, http.StatusInternalServerError, "Failed to store note", w) { + return + } + + defer stmt.Close() + + res := stmt.QueryRow(note.Content) + err = res.Scan(¬e.Id) + if httpError(err, http.StatusInternalServerError, "Failed to store note", w) { + return + } + + json, err := json.Marshal(note) + if httpError(err, http.StatusInternalServerError, "Failed to create JSON", w) { + return + } + + w.WriteHeader(http.StatusCreated) + w.Write(json) +} + +func putNote(w http.ResponseWriter, req *http.Request) { + if req.Method != http.MethodPut { + w.WriteHeader(http.StatusBadRequest) + io.WriteString(w, "Invalid method") + return + } +} + +func handleNotes(w http.ResponseWriter, req *http.Request) { + switch req.Method { + case http.MethodGet: + getNotes(w, req) + case http.MethodPost: + postNotes(w, req) + default: + w.WriteHeader(http.StatusBadRequest) + io.WriteString(w, "Invalid method") + } +} + +func main() { + log.Println("Starting NoteServer") + + log.Println("Loading database") + + var err error + db, err = sql.Open("sqlite3", "./notes.sqlite") + if err != nil { + log.Fatalln("Failed to load db:", err) + } + + log.Println("Creating missing tables") + _, err = db.Exec(`CREATE TABLE IF NOT EXISTS Notes(Id INTEGER PRIMARY KEY AUTOINCREMENT, Content TEXT)`) + if err != nil { + log.Fatalln("Failed to create table:", err) + } + + log.Println("Listening") + + http.HandleFunc("/api/notes", handleNotes) + http.HandleFunc("/api/note/", putNote) + http.ListenAndServe(":8080", nil) // TODO: configure host, port, TLS +}