diff --git a/api.go b/api.go
new file mode 100644
index 0000000..282dcd3
--- /dev/null
+++ b/api.go
@@ -0,0 +1,109 @@
+package main
+
+import (
+ "encoding/json"
+ "errors"
+ "github.com/go-chi/chi/v5"
+ "io"
+ "log/slog"
+ "net/http"
+ "os"
+ "time"
+)
+
+// HandleWrite checks for error in ResponseWriter.Write output
+func HandleWrite(_ int, err error) {
+ if err != nil {
+ slog.Error("error writing response", "error", err)
+ }
+}
+
+// GetFile returns raw contents of a file
+func GetFile(filename string, w http.ResponseWriter) {
+ fileContents, err := ReadFile(filename)
+ if err != nil {
+ if errors.Is(err, os.ErrNotExist) {
+ http.Error(w, "file not found", http.StatusNotFound)
+ } else {
+ http.Error(w, "error reading found", http.StatusNotFound)
+ }
+ return
+ }
+ HandleWrite(w.Write(fileContents))
+}
+
+// PostFile writes request's body contents to a file
+func PostFile(filename string, w http.ResponseWriter, r *http.Request) {
+ body, err := io.ReadAll(r.Body)
+ if err != nil {
+ w.WriteHeader(http.StatusInternalServerError)
+ HandleWrite(w.Write([]byte("error reading body")))
+ return
+ }
+ err = SaveFile(filename, body)
+ if err != nil {
+ w.WriteHeader(http.StatusInternalServerError)
+ HandleWrite(w.Write([]byte("error saving file")))
+ return
+ }
+ HandleWrite(w.Write([]byte("wrote to file")))
+ w.WriteHeader(http.StatusOK)
+}
+
+// GetFileList returns JSON list of filenames in a directory without extensions or path
+func GetFileList(directory string, w http.ResponseWriter) {
+ filenames, err := ListFiles(directory)
+ if err != nil {
+ http.Error(w, "error searching for files", http.StatusInternalServerError)
+ return
+ }
+ filenamesJson, err := json.Marshal(filenames)
+ if err != nil {
+ http.Error(w, "error marshaling json", http.StatusInternalServerError)
+ return
+ }
+ HandleWrite(w.Write(filenamesJson))
+}
+
+// GetDayApi returns a contents of a daily file specified in URL
+func GetDayApi(w http.ResponseWriter, r *http.Request) {
+ dayString := chi.URLParam(r, "day")
+ if dayString == "" {
+ w.WriteHeader(http.StatusBadRequest)
+ HandleWrite(w.Write([]byte("day not specified")))
+ return
+ }
+ GetFile("day/"+dayString, w)
+}
+
+// GetTodayApi runs GetFile with today's date as filename
+func GetTodayApi(w http.ResponseWriter, _ *http.Request) {
+ GetFile("day/"+time.Now().Format(time.DateOnly), w)
+}
+
+// PostTodayApi runs PostFile with today's date as filename
+func PostTodayApi(w http.ResponseWriter, r *http.Request) {
+ PostFile("day/"+time.Now().Format(time.DateOnly), w, r)
+}
+
+// GetNote returns contents of a note specified in URL
+func GetNote(w http.ResponseWriter, r *http.Request) {
+ noteString := chi.URLParam(r, "note")
+ if noteString == "" {
+ w.WriteHeader(http.StatusBadRequest)
+ HandleWrite(w.Write([]byte("note not specified")))
+ return
+ }
+ GetFile("notes/"+noteString, w)
+}
+
+// PostNote writes request's body contents to a note specified in URL
+func PostNote(w http.ResponseWriter, r *http.Request) {
+ noteString := chi.URLParam(r, "note")
+ if noteString == "" {
+ w.WriteHeader(http.StatusBadRequest)
+ HandleWrite(w.Write([]byte("note not specified")))
+ return
+ }
+ PostFile("notes/"+noteString, w, r)
+}
diff --git a/files.go b/files.go
index d20b595..252216d 100644
--- a/files.go
+++ b/files.go
@@ -1,12 +1,8 @@
package main
import (
- "encoding/json"
"errors"
- "github.com/go-chi/chi/v5"
- "io"
"log/slog"
- "net/http"
"os"
"path"
"path/filepath"
@@ -14,20 +10,12 @@ import (
"time"
)
-// HandleWrite checks for error in ResponseWriter.Write output
-func HandleWrite(_ int, err error) {
- if err != nil {
- slog.Error("error writing response", "error", err)
- }
-}
-
-// GetFile returns raw contents of a txt file in data directory
-func GetFile(filename string, w http.ResponseWriter) {
+// ReadFile returns raw contents of a file
+func ReadFile(filename string) ([]byte, error) {
filename = "data/" + path.Clean(filename) + ".txt" // Does this sanitize the path?
if _, err := os.Stat(filename); errors.Is(err, os.ErrNotExist) {
- http.Error(w, "file not found", http.StatusNotFound)
- return
+ return nil, err
}
fileContents, err := os.ReadFile(filename)
@@ -35,103 +23,50 @@ func GetFile(filename string, w http.ResponseWriter) {
slog.Error("error reading file",
"error", err,
"file", filename)
- http.Error(w, "error reading file", http.StatusInternalServerError)
- return
+ return nil, err
}
- _, err = w.Write(fileContents)
- if err != nil {
- http.Error(w, "error sending file", http.StatusInternalServerError)
- }
+ return fileContents, nil
}
-// PostFile Writes request's contents to a txt file in data directory
-// TODO: Save to trash to prevent malicious/accidental overrides?
-func PostFile(filename string, w http.ResponseWriter, r *http.Request) {
- body, err := io.ReadAll(r.Body)
- if err != nil {
- w.WriteHeader(http.StatusInternalServerError)
- HandleWrite(w.Write([]byte("error reading body")))
- return
- }
-
+// SaveFile Writes request's contents to a file
+func SaveFile(filename string, contents []byte) error {
filename = "data/" + filename + ".txt"
f, err := os.OpenFile(filename, os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
slog.Error("error opening/making file",
"error", err,
"file", filename)
- HandleWrite(w.Write([]byte("error opening or creating file")))
- w.WriteHeader(http.StatusInternalServerError)
- return
+ return err
}
-
- if _, err := f.Write(body); err != nil {
+ if _, err := f.Write(contents); err != nil {
slog.Error("error writing to file",
"error", err,
"file", filename)
- HandleWrite(w.Write([]byte("error writing to file")))
- w.WriteHeader(http.StatusInternalServerError)
- return
+ return err
}
- HandleWrite(w.Write([]byte("wrote to file")))
- w.WriteHeader(http.StatusOK)
+ return nil
}
-// ListFiles returns JSON list of filenames in a directory without extensions or path
-func ListFiles(directory string, w http.ResponseWriter) {
+// ListFiles returns slice of filenames in a directory without extensions or path
+func ListFiles(directory string) ([]string, error) {
filenames, err := filepath.Glob("data/" + directory + "/*.txt")
if err != nil {
- http.Error(w, "error searching for files", http.StatusInternalServerError)
- return
+ return nil, err
}
for i, file := range filenames {
file, _ := strings.CutSuffix(filepath.Base(file), filepath.Ext(file))
filenames[i] = file
}
- filenamesJson, err := json.Marshal(filenames)
- HandleWrite(w.Write(filenamesJson))
+ return filenames, nil
}
-// GetDay returns a day specified in URL
-func GetDay(w http.ResponseWriter, r *http.Request) {
- dayString := chi.URLParam(r, "day")
- if dayString == "" {
- w.WriteHeader(http.StatusBadRequest)
- HandleWrite(w.Write([]byte("day not specified")))
- return
- }
- GetFile("day/"+dayString, w)
+// ReadToday runs ReadFile with today's date as filename
+func ReadToday() ([]byte, error) {
+ return ReadFile("day/" + time.Now().Format(time.DateOnly))
}
-// GetToday runs GetFile with today's daily txt
-func GetToday(w http.ResponseWriter, _ *http.Request) {
- GetFile("day/"+time.Now().Format("2006-01-02"), w)
-}
-
-// PostToday runs PostFile with today's daily txt
-func PostToday(w http.ResponseWriter, r *http.Request) {
- PostFile("day/"+time.Now().Format("2006-01-02"), w, r)
-}
-
-// GetNote returns a note specified in URL
-func GetNote(w http.ResponseWriter, r *http.Request) {
- noteString := chi.URLParam(r, "note")
- if noteString == "" {
- w.WriteHeader(http.StatusBadRequest)
- HandleWrite(w.Write([]byte("note not specified")))
- return
- }
- GetFile("notes/"+noteString, w)
-}
-
-// PostNote writes request's contents to a note specified in URL
-func PostNote(w http.ResponseWriter, r *http.Request) {
- noteString := chi.URLParam(r, "note")
- if noteString == "" {
- w.WriteHeader(http.StatusBadRequest)
- HandleWrite(w.Write([]byte("note not specified")))
- return
- }
- PostFile("notes/"+noteString, w, r)
+// SaveToday runs SaveFile with today's date as filename
+func SaveToday(contents []byte) error {
+ return SaveFile("day/"+time.Now().Format(time.DateOnly), contents)
}
diff --git a/log.go b/log.go
deleted file mode 100644
index 6c1ed4d..0000000
--- a/log.go
+++ /dev/null
@@ -1,57 +0,0 @@
-package main
-
-import (
- "io"
- "log/slog"
- "net/http"
- "os"
- "strings"
- "time"
-)
-
-// AppendLog adds the input string to the end of the log file with a timestamp
-func appendLog(input string) error {
- t := time.Now().Format("2006-01-02 15:04") // yyyy-mm-dd HH:MM
- filename := "data/log.txt"
-
- f, err := os.OpenFile(filename, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
- if err != nil {
- slog.Error("error opening/making file",
- "error", err,
- "file", filename)
- return err
- }
-
- input = strings.Replace(input, "\n", "", -1) // Remove newlines to maintain structure
- if _, err := f.Write([]byte(t + " | " + input + "\n")); err != nil {
- slog.Error("error appending to file",
- "error", err,
- "file", filename)
- return err
- }
- if err := f.Close(); err != nil {
- slog.Error("error closing file",
- "error", err,
- "file", filename)
- return err
- }
- return nil
-}
-
-func PostLog(w http.ResponseWriter, r *http.Request) {
- body, err := io.ReadAll(r.Body)
- if err != nil {
- w.WriteHeader(http.StatusInternalServerError)
- w.Write([]byte("error reading body"))
- return
- }
- err = appendLog(string(body))
- if err != nil {
- w.WriteHeader(http.StatusInternalServerError)
- w.Write([]byte("error appending to log"))
- return
- }
-
- w.WriteHeader(http.StatusOK)
- w.Write([]byte("appended to log"))
-}
diff --git a/pages/base.html b/pages/base.html
new file mode 100644
index 0000000..7c697c2
--- /dev/null
+++ b/pages/base.html
@@ -0,0 +1,38 @@
+{{define "header"}}
+ Today is a place🌺 Hibiscus.txt
+
Today is a place
-Today is a place
-It's probably not your fault, but something broke
+Today is a place
-