package server import ( "caj-larsson/bog/dataswamp" "caj-larsson/bog/dataswamp/namespace" "caj-larsson/bog/dataswamp/swampfile" fs_swampfile "caj-larsson/bog/infrastructure/fs/swampfile" sql_namespace "caj-larsson/bog/infrastructure/sqlite/namespace" "caj-larsson/bog/infrastructure/system_time" "caj-larsson/bog/util" "net/http" "strconv" "text/template" "time" ) type Router interface { HandleFunc(pattern string, handler func(http.ResponseWriter, *http.Request)) ServeHTTP(http.ResponseWriter, *http.Request) } type Bog struct { router Router adminRouter Router file_service dataswamp.SwampFileService address string adminAddress string logger util.Logger } func buildFileDataRepository(config FileConfig) swampfile.Repository { r, err := fs_swampfile.NewRepository(config.Path) if err != nil { panic(err) } return r } func buildNamespaceRepository(config DatabaseConfig) namespace.Repository { if config.Backend != "sqlite" { panic("Can only handle sqlite") } return sql_namespace.NewRepository(config.Connection) } func (b *Bog) fileHandler(w http.ResponseWriter, r *http.Request) { ref := swampfile.FileReference{r.URL.Path, r.Header["User-Agent"][0]} if r.URL.Path == "/" { http.NotFound(w, r) return } switch r.Method { case "GET": swamp_file, err := b.file_service.OpenOutFile(ref) if err == swampfile.ErrNotExists { templ, err := template.ParseFiles("server/views/upload.html") if err != nil { panic(err) } w.WriteHeader(404) err = templ.Execute(w, nil) if err != nil { panic(err) } return } if err != nil { panic(err) } b.logger.Info("Serving file '%s' of size %d from '%s'", ref.Path, swamp_file.Size(), ref.UserAgent) w.Header().Set("Content-Disposition", "attachment") http.ServeContent(w, r, swamp_file.Path(), swamp_file.Modified(), swamp_file) case "POST": fallthrough case "PUT": size_str := r.Header["Content-Length"][0] size, err := strconv.ParseInt(size_str, 10, 64) if err != nil { w.WriteHeader(422) return } b.logger.Info("Recieving file '%s' of size %d from '%s'", ref.Path, size, ref.UserAgent) err = b.file_service.SaveFile(ref, r.Body, size) if err != nil { panic(err) } } return } func (b *Bog) dashboardHandler(w http.ResponseWriter, r *http.Request) { templ, err := template.ParseFiles("server/views/dashboard.html") if err != nil { panic(err) } stats := b.file_service.NamespaceStats() err = templ.Execute(w, stats) if err != nil { panic(err) } } func (b *Bog) routes() { b.router.HandleFunc("/", b.fileHandler) b.adminRouter.HandleFunc("/", b.dashboardHandler) } func (b *Bog) cleanNamespaces() { for true { b.file_service.CleanUpExpiredFiles() time.Sleep(time.Minute * 10) } } func New(config *Configuration) *Bog { b := new(Bog) b.address = config.Server.bindAddress() b.adminAddress = config.Admin.bindAddress() fsSwampRepo := buildFileDataRepository(config.File) nsRepo := buildNamespaceRepository(config.Database) logger := ServerLogger{Debug} ns_svc := namespace.NewNamespaceService( nsRepo, logger, system_time.Clock{}, config.Quota.ParsedDuration(), config.Quota.ParsedSizeBytes(), ) b.file_service = *dataswamp.NewSwampFileService( *ns_svc, fsSwampRepo, logger, ) b.logger = logger b.router = new(http.ServeMux) b.adminRouter = new(http.ServeMux) b.routes() return b } func (b *Bog) Run() { b.logger.Info("Starting bog on address: %s", b.address) go b.cleanNamespaces() go func() { http.ListenAndServe(b.adminAddress, b.adminRouter) }() http.ListenAndServe(b.address, b.router) }