diff options
| author | horus_arch | 2015-05-08 01:47:01 +0200 |
|---|---|---|
| committer | horus_arch | 2015-05-08 01:47:01 +0200 |
| commit | 57fd242908857312d304726d128cd8293c1b015a (patch) | |
| tree | 9beddd9b758d27d3762833287c4e4ac9486f2b7f | |
| parent | fb7050acf2545c9d222d2c014460e4fcad5cc462 (diff) | |
| download | scribbled-57fd242908857312d304726d128cd8293c1b015a.tar.gz | |
Now with source files.
| -rw-r--r-- | .gitignore | 1 | ||||
| -rw-r--r-- | scribbled/api.go | 107 | ||||
| -rw-r--r-- | scribbled/config.go | 83 | ||||
| -rw-r--r-- | scribbled/db.go | 14 | ||||
| -rw-r--r-- | scribbled/index.go | 41 | ||||
| -rw-r--r-- | scribbled/main.go | 77 | ||||
| -rw-r--r-- | scribbled/server.go | 70 | ||||
| -rw-r--r-- | scribbled/struct.go | 22 | ||||
| -rw-r--r-- | scribbled/util.go | 30 | ||||
| -rw-r--r-- | scribbled/view.go | 78 |
10 files changed, 522 insertions, 1 deletions
@@ -1,4 +1,3 @@ -scribbled *.swp *.db db/ diff --git a/scribbled/api.go b/scribbled/api.go new file mode 100644 index 0000000..308cb57 --- /dev/null +++ b/scribbled/api.go @@ -0,0 +1,107 @@ +package main + +import ( + "encoding/json" + "log" + "net/http" +) + +func ApiNewHandler(w http.ResponseWriter, r *http.Request) { + err := r.ParseForm() + if err != nil { + log.Println("ERROR:", err.Error()) + ErrorResponse(w, err.Error(), http.StatusInternalServerError) + return + } + + note := Note{} + err = decoder.Decode(¬e, r.PostForm) + if err != nil { + log.Println("ERROR:", err.Error()) + ErrorResponse(w, err.Error(), http.StatusInternalServerError) + return + } + + data, err := json.Marshal(note) + if err != nil { + log.Println("ERROR:", err.Error()) + ErrorResponse(w, err.Error(), http.StatusInternalServerError) + return + } + + err = Conf.DB.Put([]byte(note.Title), data, nil) + if err != nil { + log.Println("ERROR:", err.Error()) + ErrorResponse(w, err.Error(), http.StatusInternalServerError) + return + } + + Response(w, map[string]interface{}{"Title": note.Title, "Url": Conf.VirtualRoot + "/view/" + note.Title}) +} + +func ApiGetHandler(w http.ResponseWriter, r *http.Request) { + err := r.ParseForm() + if err != nil { + log.Println("ERROR:", err.Error()) + ErrorResponse(w, err.Error(), http.StatusInternalServerError) + return + } + + note := Note{} + err = decoder.Decode(¬e, r.Form) + if err != nil { + log.Println("ERROR:", err.Error()) + ErrorResponse(w, err.Error(), http.StatusInternalServerError) + return + } + + data, err := Conf.DB.Get([]byte(note.Title), nil) + if err != nil { + log.Println("ERROR:", err.Error()) + ErrorResponse(w, "Not Found", http.StatusNotFound) + return + } + + err = json.Unmarshal(data, ¬e) + if err != nil { + log.Println("ERROR:", err.Error()) + ErrorResponse(w, err.Error(), http.StatusInternalServerError) + return + } + + Response(w, map[string]interface{}{"scribble": note, "Url": Conf.VirtualRoot + "/" + note.Title}) + //log.Println(string(RenderMarkdown([]byte(note.Post)))) +} + +func SearchHandler(w http.ResponseWriter, r *http.Request) { + err := r.ParseForm() + if err != nil { + log.Println("ERROR:", err.Error()) + ErrorResponse(w, err.Error(), http.StatusInternalServerError) + return + } + + note := Note{} + note.VirtualRoot = Conf.VirtualRoot + + /* + val := r.URL.Query() + key := val["q"][0] + */ + key := r.Form.Get("q") + _, err = Conf.DB.Get([]byte(key), nil) + http.Redirect(w, r, Conf.VirtualRoot+"/"+key, 301) + /* + if err != nil { + http.Redirect(w, r, Conf.VirtualRoot+"/"+key, 301) + note.NotFound = true + note.Title = key + err = ExecTemplate("index.html", w, r, note) + if err != nil { + log.Println("ERROR:", err.Error()) + } + return + } + http.Redirect(w, r, Conf.VirtualRoot+"/"+key, 301) + */ +} diff --git a/scribbled/config.go b/scribbled/config.go new file mode 100644 index 0000000..c6598b1 --- /dev/null +++ b/scribbled/config.go @@ -0,0 +1,83 @@ +package main + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "log" + "os" + "strings" +) + +func NewConfiguration(path string) *Configuration { + file, err := os.Open(path) + if err != nil { + log.Fatal("ERROR: Error opening config file. Please provide on with -config") + os.Exit(-1) + } + + decoder := json.NewDecoder(file) + conf := &Configuration{} + err = decoder.Decode(conf) + if err != nil { + log.Fatal("ERROR: Error parsing config file.", err) + os.Exit(-1) + } + + return conf +} + +func (conf *Configuration) configInit() { + if !DirExists(conf.StaticDir) { + err := os.MkdirAll(conf.StaticDir, 0755) + if err != nil { + log.Fatal("ERROR: Can't create directory "+conf.StaticDir, err) + os.Exit(-1) + } + } + if !DirExists(conf.TemplateDir) { + err := os.MkdirAll(conf.TemplateDir, 0755) + if err != nil { + log.Fatal("ERROR: Can't create directory "+conf.TemplateDir, err) + os.Exit(-1) + } + } + if !DirExists(conf.DatabasePath) { + err := os.MkdirAll(conf.DatabasePath, 0755) + if err != nil { + log.Fatal("ERROR: Can't create directory "+conf.DatabasePath, err) + os.Exit(-1) + } + } + if !strings.HasPrefix(conf.VirtualRoot, "/") { + conf.VirtualRoot = "/" + conf.VirtualRoot + } + if strings.HasSuffix(conf.VirtualRoot, "/") { + conf.VirtualRoot = strings.TrimSuffix(conf.VirtualRoot, "/") + } +} + +func GenConfig(path string) { + if path == "" { + path = "config.json" + } + Conf := Configuration{} + Conf.StaticDir = "static" + Conf.TemplateDir = "templates" + Conf.Ip = "127.0.0.1" + Conf.Port = 8765 + Conf.VirtualRoot = "/" + Conf.DatabasePath = "db" + + file, err := json.MarshalIndent(&Conf, "", " ") + if err != nil { + log.Fatal("ERROR: Error parsing json.", err) + } + + err = ioutil.WriteFile(path, file, 0644) + if err != nil { + log.Fatal("ERROR: Writing file to "+path, err) + } + + fmt.Println("INFO: Generated new config file (" + path + ").") +} diff --git a/scribbled/db.go b/scribbled/db.go new file mode 100644 index 0000000..db4279f --- /dev/null +++ b/scribbled/db.go @@ -0,0 +1,14 @@ +package main + +import ( + "github.com/syndtr/goleveldb/leveldb" + "log" +) + +func (c *Configuration) NewDatabase() *leveldb.DB { + db, err := leveldb.OpenFile(c.DatabasePath, nil) + if err != nil { + log.Panic("ERROR: Error opening database. ", err) + } + return db +} diff --git a/scribbled/index.go b/scribbled/index.go new file mode 100644 index 0000000..9e93720 --- /dev/null +++ b/scribbled/index.go @@ -0,0 +1,41 @@ +package main + +import ( + "encoding/json" + "github.com/gorilla/mux" + "log" + "net/http" +) + +func IndexHandler(w http.ResponseWriter, r *http.Request) { + err := ExecTemplate("index.html", w, r) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } +} + +func ScribbleHandler(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + + note := Note{} + note.Title = vars["title"] + data, err := Conf.DB.Get([]byte(note.Title), nil) + if err != nil { + log.Println("ERROR:", err.Error()) + NotFoundHandler(w, r) + return + } + + err = json.Unmarshal(data, ¬e) + if err != nil { + log.Println("ERROR:", err.Error()) + ErrorResponse(w, err.Error(), http.StatusInternalServerError) + return + } + err = ExecTemplate("index.html", w, r, note) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } +} diff --git a/scribbled/main.go b/scribbled/main.go new file mode 100644 index 0000000..bc6f347 --- /dev/null +++ b/scribbled/main.go @@ -0,0 +1,77 @@ +package main + +import ( + "flag" + "github.com/gorilla/mux" + "github.com/gorilla/schema" + "github.com/gorilla/sessions" + "html/template" + "log" + "net/http" + "os" + "strconv" +) + +var decoder = schema.NewDecoder() +var store = sessions.NewCookieStore([]byte(os.Getenv("SCRIBBLED_SECRET"))) +var mainTempl *template.Template +var Conf *Configuration + +func add(x, y int) int { + return x + y +} + +func main() { + + conf_f := flag.String("config", "config.json", "Path to the configuration file.") + genConf_f := flag.Bool("genconfig", false, "Generate new configuration file and exit.") + flag.Parse() + + if *genConf_f { + GenConfig(*conf_f) + return + } + + Conf = NewConfiguration(*conf_f) + Conf.configInit() + + Conf.DB = Conf.NewDatabase() + defer func() { + if r := recover(); r != nil { + log.Println("Recovering", r) + defer Conf.DB.Close() + } + }() + defer Conf.DB.Close() + + mainTempl = template.Must(template.New("global").Funcs(template.FuncMap{"add": add, "RenderMarkdown": RenderMarkdown}).ParseGlob(Conf.TemplateDir + "/*.html")) + + decoder.IgnoreUnknownKeys(true) + store.Options = &sessions.Options{ + Path: "/", + MaxAge: 86400, + HttpOnly: true, + } + + r := mux.NewRouter() + r.HandleFunc(Conf.VirtualRoot+"/", IndexHandler) + r.HandleFunc(Conf.VirtualRoot+"/search", SearchHandler) + r.HandleFunc(Conf.VirtualRoot+"/api/new", ApiNewHandler) + r.HandleFunc(Conf.VirtualRoot+"/api/get", ApiGetHandler) + r.HandleFunc(Conf.VirtualRoot+"/view/{title}", ViewHandler) + r.HandleFunc(Conf.VirtualRoot+"/help", PageHandler) + r.HandleFunc(Conf.VirtualRoot+"/{title}", ScribbleHandler) + r.PathPrefix(Conf.VirtualRoot + "/static/").Handler(http.StripPrefix("/static/", http.FileServer(http.Dir(Conf.StaticDir)))) + r.NotFoundHandler = http.HandlerFunc(NotFoundHandler) + + http.Handle("/", r) + + ip := Conf.Ip + port := os.Getenv("PORT") + if port == "" { + port = strconv.Itoa(Conf.Port) + } + + log.Println("Starting server listening on " + ip + ":" + port) + log.Panic(http.ListenAndServe(ip+":"+port, nil)) +} diff --git a/scribbled/server.go b/scribbled/server.go new file mode 100644 index 0000000..d1ea154 --- /dev/null +++ b/scribbled/server.go @@ -0,0 +1,70 @@ +package main + +import ( + "encoding/json" + "github.com/gorilla/mux" + "log" + "net/http" +) + +func ExecTemplate(template string, w http.ResponseWriter, r *http.Request, msg ...Note) error { + index := mainTempl.Lookup(template) + + note := Note{} + if len(msg) > 0 { + note = msg[0] + } + note.VirtualRoot = Conf.VirtualRoot + + /* + if note.Title == "" { + note.Title = "Welcome" + } + */ + err := index.ExecuteTemplate(w, template, note) + if err != nil { + log.Println("ERROR: ", err) + return err + } + return nil +} + +func ErrorResponse(w http.ResponseWriter, message string, status int) { + Response(w, map[string]interface{}{"error": message}, status) +} + +func Response(w http.ResponseWriter, data map[string]interface{}, status ...int) { + var responseStatus int + if len(status) > 0 { + responseStatus = status[0] + } else { + responseStatus = http.StatusOK + } + + w.WriteHeader(responseStatus) + w.Header().Set("Content-Type", "application/json") + + resp, _ := json.Marshal( + map[string]interface{}{ + "success": responseStatus == http.StatusOK, + "status": responseStatus, + "data": data, + }) + + w.Write(resp) +} + +func NotFoundHandler(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(404) + + vars := mux.Vars(r) + note := Note{} + note.Title = vars["title"] + note.NotFound = true + + err := ExecTemplate("index.html", w, r, note) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } +} diff --git a/scribbled/struct.go b/scribbled/struct.go new file mode 100644 index 0000000..61d56f9 --- /dev/null +++ b/scribbled/struct.go @@ -0,0 +1,22 @@ +package main + +import ( + "github.com/syndtr/goleveldb/leveldb" +) + +type Configuration struct { + StaticDir string + TemplateDir string + Port int + Ip string + VirtualRoot string + DatabasePath string + DB *leveldb.DB +} + +type Note struct { + Title string + Post string + NotFound bool + VirtualRoot string +} diff --git a/scribbled/util.go b/scribbled/util.go new file mode 100644 index 0000000..a1cff0c --- /dev/null +++ b/scribbled/util.go @@ -0,0 +1,30 @@ +package main + +import ( + "os" +) + +func DirExists(filename string) bool { + fileInfo, err := os.Stat(filename) + return err == nil && fileInfo.IsDir() +} + +func toString(slc []string) string { + var s string + for _, v := range slc { + s = s + " " + v + } + return s +} + +func Remove(slc []string, s string) []string { + var id int + for k, v := range slc { + if v == s { + id = k + break + } + } + slc = append(slc[:id], slc[id+1:]...) + return slc +} diff --git a/scribbled/view.go b/scribbled/view.go new file mode 100644 index 0000000..b341ee1 --- /dev/null +++ b/scribbled/view.go @@ -0,0 +1,78 @@ +package main + +import ( + "encoding/json" + "fmt" + "github.com/gorilla/mux" + "github.com/microcosm-cc/bluemonday" + "github.com/russross/blackfriday" + "html/template" + "io/ioutil" + "log" + "net/http" +) + +func RenderMarkdown(args ...interface{}) template.HTML { + unsafe := blackfriday.MarkdownCommon([]byte(fmt.Sprintf("%s", args...))) + html := bluemonday.UGCPolicy().SanitizeBytes(unsafe) + + return template.HTML(html) +} + +func ViewHandler(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + + note := Note{} + note.Title = vars["title"] + + /* + err := decoder.Decode(¬e, r.Form) + if err != nil { + log.Println("ERROR:", err.Error()) + ErrorResponse(w, err.Error(), http.StatusInternalServerError) + return + } + */ + + data, err := Conf.DB.Get([]byte(note.Title), nil) + if err != nil { + log.Println("ERROR:", err.Error()) + NotFoundHandler(w, r) + return + } + + err = json.Unmarshal(data, ¬e) + if err != nil { + log.Println("ERROR:", err.Error()) + ErrorResponse(w, err.Error(), http.StatusInternalServerError) + return + } + + err = ExecTemplate("view.html", w, r, note) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } +} + +func PageHandler(w http.ResponseWriter, r *http.Request) { + page := "Help Page" + note := Note{} + + file, err := ioutil.ReadFile(Conf.TemplateDir + "/help.md") + if err != nil { + log.Println("ERROR:", err.Error()) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + note.Title = page + note.Post = string(file) + + err = ExecTemplate("view.html", w, r, note) + if err != nil { + log.Println("ERROR:", err.Error()) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } +} |
