141 lines
3.0 KiB
Go
141 lines
3.0 KiB
Go
|
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
|
||
|
}
|