Browse Source

initial commit

main
emile 1 year ago
commit
f76253a739
Signed by: emile GPG Key ID: 4D8DD313A751DED7
  1. 1
      .gitignore
  2. 4
      README.md
  3. 5
      go.mod
  4. 2
      go.sum
  5. 192
      main.go
  6. 7
      static/bootstrap.min.css

1
.gitignore

@ -0,0 +1 @@
notes/*

4
README.md

@ -0,0 +1,4 @@
# notes
A super simple, no functionality notetaking application.

5
go.mod

@ -0,0 +1,5 @@
module git.darknebu.la/emile/notes
go 1.15
require github.com/gorilla/mux v1.8.0

2
go.sum

@ -0,0 +1,2 @@
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=

192
main.go

@ -0,0 +1,192 @@
package main
import (
"os"
"fmt"
"net/http"
"github.com/gorilla/mux"
"html/template"
"strings"
"flag"
"io/ioutil"
)
var (
tmpl string // tmpl defines the template used for rendering the notes
path string // path defines the path where the notes should be stored
)
func main() {
initTemplate()
initFlags()
r := mux.NewRouter()
// getting rid of this "favicon.ico could not be found error message"
r.HandleFunc("/favicon.ico", func(w http.ResponseWriter, r *http.Request) {fmt.Fprintf(w, "%s", "")})
// static files
fs := http.FileServer(http.Dir("./static"))
r.PathPrefix("/static/").Handler(http.StripPrefix("/static/", fs))
// the actual note handlers
r.HandleFunc("/{path:[a-zA-Z0-9/]*}", pathGetHandler).Methods("GET")
r.HandleFunc("/{path:[a-zA-Z0-9/]*}", pathPostHandler).Methods("POST")
http.ListenAndServe(":8080", r)
}
// pathGetHandler handles GET requests to any path, thus handling the notes themselves
func pathGetHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
// fetch all possible notes
treeSlice := recursiveDive("")
// read the current note
file := readFile(vars["path"])
templateContent := map[string]interface{}{
"Tree": treeSlice,
"File": file,
"Path": fmt.Sprintf("/%s", vars["path"]),
}
// define the template used to render the frontend
t := template.New("")
t, err := t.Parse(tmpl) // tmpl is a global var defined using initTemplate()
if err != nil {
fmt.Printf("error parsing the template: %s", err)
return
}
// execute the template using the values defined in the templateContent map
err = t.ExecuteTemplate(w, "", templateContent)
if err != nil {
fmt.Printf("error executing the template: %s", err)
return
}
}
// pathPostHandler handles POST requests to the individual notes. This gets
// triggered when a note gets saved
func pathPostHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
// parse the post form
if err := r.ParseForm(); err != nil {
fmt.Printf("error parsing the post form: %s", err)
return
}
// extrace the file and covert it to a byte array for writing
file := r.FormValue("file")
d1 := []byte(file)
// write the notes content to its file
err := ioutil.WriteFile(fmt.Sprintf("%s/%s.txt", path, vars["path"]), d1, 0644)
if err != nil {
fmt.Printf("error writing the note to it's file: %s", err)
}
http.Redirect(w, r, fmt.Sprintf("/%s", vars["path"]), http.StatusSeeOther)
}
// recursiveDive recursively iterates over all notes starting at the given
// divePath
func recursiveDive(divePath string) []string {
var returnSlice []string
c, err := ioutil.ReadDir(fmt.Sprintf("%s/%s", path, divePath))
if err != nil {
fmt.Printf("error reading the dir: %s", err)
return []string{}
}
for _, entry := range c {
if entry.IsDir() {
newDivePath := fmt.Sprintf("%s/%s", divePath, entry.Name())
newElements := recursiveDive(newDivePath)
// recursively call the function for each dir in the current dir
for _, element := range newElements {
returnSlice = append(returnSlice, strings.TrimSuffix(element, ".txt"))
}
} else {
returnSlice = append(returnSlice, strings.TrimSuffix(fmt.Sprintf("%s/%s", divePath, entry.Name()), ".txt"))
}
}
return returnSlice
}
// readFile reads a files content and returns it. Amazing, isn't it?
func readFile(filePath string) string {
fullFilePath := fmt.Sprintf("%s/%s.txt", path, filePath)
dat, err := ioutil.ReadFile(fullFilePath)
if err != nil {
fmt.Println(err)
// handle the error
if strings.Contains(err.Error(), "no such file or directory") {
// if the file path contains a /, strip stuff and create the folder...
if strings.Contains(fullFilePath, "/") {
extractedPath := fullFilePath[0:strings.LastIndex(fullFilePath, "/")]
err := os.MkdirAll(extractedPath, 0755)
if err != nil {
fmt.Println(err)
}
}
// ...then create the actual file
fmt.Printf("Creating %s\n", fullFilePath)
_, err = os.Create(fullFilePath)
if err != nil {
fmt.Println(err)
}
}
}
return string(dat)
}
func initFlags() {
flag.StringVar(&path, "path", ".", "folder where the notes should be stored")
flag.Parse()
}
// initTemplate is down here, as it may become a bit longer and the focus
// should stay on the code above
func initTemplate() {
tmpl = `<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="/static/bootstrap.min.css">
<title>Notes</title>
</head>
<body>
<div class="row m-1 mt-3">
<div class="col-4">
<div class="list-group">
{{ range .Tree }}
<a href="{{ . }}" class="list-group-item list-group-item-action">{{ . -}}</a>
{{ end }}
</div>
</div>
<div class="col-8">
<div class="mb-3">
<form method="POST" action="{{ .Path }}">
<textarea class="form-control" id="file" name="file" rows="15">{{ .File }}</textarea>
<button type="submit" class="btn btn-outline-secondary mt-3">Save</button>
<button type="submit" formaction="/outoutout" class="btn btn-outline-danger mt-3">Delete</button>
</form>
</div>
</div>
</div>
</body>
</html>
`
}

7
static/bootstrap.min.css

File diff suppressed because one or more lines are too long
Loading…
Cancel
Save