Browse Source

initial commit

main
emile 2 months ago
commit
f76253a739
Signed by: emile GPG Key ID: 4D8DD313A751DED7
6 changed files with 211 additions and 0 deletions
  1. +1
    -0
      .gitignore
  2. +4
    -0
      README.md
  3. +5
    -0
      go.mod
  4. +2
    -0
      go.sum
  5. +192
    -0
      main.go
  6. +7
    -0
      static/bootstrap.min.css

+ 1
- 0
.gitignore View File

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

+ 4
- 0
README.md View File

@@ -0,0 +1,4 @@
# notes

A super simple, no functionality notetaking application.


+ 5
- 0
go.mod View File

@@ -0,0 +1,5 @@
module git.darknebu.la/emile/notes

go 1.15

require github.com/gorilla/mux v1.8.0

+ 2
- 0
go.sum View File

@@ -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
- 0
main.go View File

@@ -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
- 0
static/bootstrap.min.css
File diff suppressed because it is too large
View File


Loading…
Cancel
Save