package contented import ( "encoding/json" "log" "net/http" "os" "path/filepath" "strings" "github.com/boltdb/bolt" ) var SERVER_HEADER string = `contented/0.1` type ServerPublicProperties struct { AppTitle string MaxUploadBytes int64 } type ServerOptions struct { DataDirectory string DBPath string ServerPublicProperties } type Server struct { opts ServerOptions db *bolt.DB metadataBucket []byte } func NewServer(opts *ServerOptions) (*Server, error) { s := &Server{ opts: *opts, metadataBucket: []byte(`METADATA`), } b, err := bolt.Open(opts.DBPath, 0644, bolt.DefaultOptions) if err != nil { return nil, err } err = b.Update(func(tx *bolt.Tx) error { _, err := tx.CreateBucketIfNotExists(s.metadataBucket) return err }) if err != nil { return nil, err } s.db = b return s, nil } func (this *Server) handleView(w http.ResponseWriter, r *http.Request, fileID string) error { // Load file f, err := os.Open(filepath.Join(this.opts.DataDirectory, fileID)) if err != nil { return err } defer f.Close() // Load metadata m, err := this.Metadata(fileID) if err != nil { return err } // ServeContent only uses the filename to get the mime type, which we can // set accurately (including blacklist) w.Header().Set(`Content-Type`, m.MimeType) if m.MimeType == `application/octet-stream` { w.Header().Set(`Content-Disposition`, `attachment; filename="`+m.Filename+`"`) } http.ServeContent(w, r, "", m.UploadTime, f) return nil } func (this *Server) handleInformation(w http.ResponseWriter, fileID string) { m, err := this.Metadata(fileID) if err != nil { if os.IsNotExist(err) { http.Error(w, "Not found", 404) return } log.Println(err.Error()) http.Error(w, "Internal error", 500) return } this.serveJsonObject(w, m) } func (this *Server) serveJsonObject(w http.ResponseWriter, o interface{}) { jb, err := json.Marshal(o) if err != nil { log.Println(err.Error()) http.Error(w, "Internal error", 500) return } w.Header().Set(`Content-Type`, `application/json`) w.WriteHeader(200) w.Write(jb) } func (this *Server) handleAbout(w http.ResponseWriter) { this.serveJsonObject(w, this.opts.ServerPublicProperties) } func (this *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { w.Header().Set(`Server`, SERVER_HEADER) if this.opts.MaxUploadBytes > 0 { r.Body = http.MaxBytesReader(w, r.Body, this.opts.MaxUploadBytes) } if r.Method == "GET" && strings.HasPrefix(r.URL.Path, `/view/`) { err := this.handleView(w, r, r.URL.Path[6:]) if err != nil { log.Printf("%s View failed: %s\n", r.RemoteAddr, err.Error()) if os.IsNotExist(err) { http.Error(w, "File not found", 404) } else { http.Error(w, "Couldn't provide content", 500) } } } else if r.Method == "GET" && strings.HasPrefix(r.URL.Path, `/info/`) { this.handleInformation(w, r.URL.Path[6:]) } else if r.Method == "GET" && r.URL.Path == `/about` { this.handleAbout(w) } else if r.Method == "POST" && r.URL.Path == `/upload` { this.handleUpload(w, r) } else { // TODO embed into binary http.FileServer(http.Dir(`static`)).ServeHTTP(w, r) } }