// Run the application, start http and scp server. func Run(appConfig Config) { config = appConfig // Connect to DB db, err := lib.Connect() if err != nil { fmt.Printf("Failed to connect to db") return } // Logging log := logrus.New() log.Level = logrus.DebugLevel log.Out = os.Stdout log.Formatter = &logrus.TextFormatter{} // Websockets ws := ws.NewServer() // Shared dependencies between all controller deps := dependencies.Dependencies{ Fs: afero.NewOsFs(), Logger: log, DB: db, WS: ws, } ws.Dependencies = &deps go ws.Start() // // Start SCP // scp := scp.Server{} // scp.DB = deps.DB // scp.Logger = deps.Logger // scp.CertPath = "certs/scp.rsa" // scp.BindAddr = config.SCPBindAddr // go scp.ListenAndServe() if config.Secure { c := autocert.DirCache("certs") m := autocert.Manager{ Cache: c, Prompt: autocert.AcceptTOS, HostPolicy: autocert.HostWhitelist("x.zqz.ca"), } s := &http.Server{ Addr: config.HTTPBindAddr, TLSConfig: &tls.Config{GetCertificate: m.GetCertificate}, } deps.Info("Listening for HTTP1.1 Connections", "addr", ":3001") deps.Info("Listening for HTTP2 Connections", "addr", config.HTTPBindAddr) go http.ListenAndServe(":3001", secureRedirect()) s.ListenAndServeTLS("", "") } else { deps.Info("Listening for HTTP1.1 Connections", "addr", config.HTTPBindAddr) http.ListenAndServe(config.HTTPBindAddr, Routes(deps)) } }
// BuildFile builds a file from chunks. func BuildFile(deps dependencies.Dependencies, f *models.File) (io.ReadSeeker, error) { chunks, err := models.Chunks( deps.DB, qm.Where("file_id=$1", f.ID), qm.OrderBy("position asc"), ).All() var hashes []string for _, c := range chunks { hashes = append(hashes, c.Hash) } if ok, err := chunksExist(hashes); !ok { deps.Error("Missing chunks!", "id", f.ID, "name", f.Name) return nil, err } if err != nil { fmt.Println("Failed to find chunks for file:", f.ID) return nil, err } fs := deps.Fs fullFilePath := filepath.Join("files", f.Hash) fullFile, err := fs.Create(fullFilePath) if err != nil { fmt.Println("Failed because", err) return nil, err } defer fullFile.Close() fullFileBuffer := &bytes.Buffer{} mw := io.MultiWriter(fullFile, fullFileBuffer) for _, c := range chunks { fmt.Println("pos:", c.Position) path := filepath.Join("files", "chunks", c.Hash) chunkData, err := fs.Open(path) if err != nil { fmt.Println("chunk Failed because", err) return nil, err } _, err = io.Copy(mw, chunkData) chunkData.Close() if err != nil { fmt.Println("Failed to copy chunk to full file") } } fmt.Println("Finished building file") // for _, c := range chunks { // path := filepath.Join("files", "chunks", c.Hash) // if err != nil { // fmt.Println("Failed to delete chunk entry:", c.ID) // } // fs.Remove(path) // } bs := bytes.NewReader(fullFileBuffer.Bytes()) return bs, nil }
// CompleteFile builds the file from chunks and then generates thumbnails func CompleteFile(deps dependencies.Dependencies, f *models.File) error { deps.Info("Processing File", "name", f.Name, "id", f.ID) tx, err := deps.DB.Begin() if err != nil { deps.Error("Failed to create transaction") return err } if err = f.Reload(tx); err != nil { tx.Rollback() return errors.Wrap(err, "Failed to reload the file") } if f.State == lib.FileProcessing { tx.Rollback() return errors.Wrap(err, "This file is already being processed") } // Delete all thumbnails err = models.Thumbnails(tx, qm.Where("file_id=?", f.ID)).DeleteAll() if err != nil { deps.Info("No previous thumnails") } f.State = lib.FileProcessing if err = f.Update(tx, "state"); err != nil { tx.Rollback() return errors.Wrap(err, "Failed to update state") } reader, err := BuildFile(deps, f) if err != nil { tx.Rollback() return errors.Wrap(err, "Failed to complete building file") } thumbHash, thumbSize, err := CreateThumbnail(deps, reader) if err != nil { tx.Rollback() return errors.Wrap(err, "Failed to create thumbnail") } if len(thumbHash) == 0 { tx.Rollback() return errors.Wrap(err, "No thumbnail created") } t := models.Thumbnail{ Hash: thumbHash, Size: thumbSize, FileID: f.ID, } if err = t.Insert(tx); err != nil { tx.Rollback() return errors.Wrap(err, "Failed to insert Thumbnail") } f.State = lib.FileFinished if err = f.Update(tx, "state"); err != nil { tx.Rollback() return errors.Wrap(err, "Failed to set state") } if err = tx.Commit(); err != nil { return errors.Wrap(err, "Failed to commit transaction") } if err = Cleanup(deps, f); err != nil { return errors.Wrap(err, "Failed to cleanup file") } deps.Info("Processed File", "name", f.Name, "id", f.ID) return nil }
// CreateThumnail builds a JPG thumbnail and can rotate if an exif bit is set. func CreateThumbnail(deps dependencies.Dependencies, r io.ReadSeeker) (string, int, error) { raw, format, err := image.Decode(r) if format == "" { return "", 0, nil } if format == "jpeg" || format == "jpg" { deps.Debug("Received JPG") orientation, err := readOrientation(r) if err == nil { deps.Debug("Rotating JPG", "orientation", orientation) raw = rotate(raw, orientation) } } deps.Debug("Thumbnail format", "fmt", format) if err != nil { deps.Error("Failed to decode image") return "", 0, err } fs := deps.Fs tmpFilePath := lib.TempFilePath("thumbnail") tmpFile, err := fs.Create(tmpFilePath) if err != nil { deps.Error("Failed to create temp file", "path", tmpFilePath) return "", 0, err } // Make sure we close. closeTmpFile := func() { if tmpFile != nil { tmpFile.Close() tmpFile = nil } } defer closeTmpFile() h := sha1.New() var wc writeCounter mw := io.MultiWriter(tmpFile, h, wc) // Generate Thumbnail image data dst := imaging.Fill(raw, 200, 200, imaging.Center, imaging.Lanczos) // Write it err = imaging.Encode(mw, dst, imaging.JPEG) if err != nil { deps.Error("Failed to encode data") return "", 0, err } hash := fmt.Sprintf("%x", h.Sum(nil)) deps.Debug("Thumbnail hash", "hash:", hash) newPath := lib.LocalPath(hash) // Move temp thumbnail to final destination. err = os.Rename(tmpFilePath, newPath) if err != nil { deps.Error("Failed to rename file") // Todo delete file return hash, int(wc), err } // Set permissons err = fs.Chmod(newPath, 0644) if err != nil { deps.Error("Failed to set permissions", "path", newPath) // Todo delete file return hash, int(wc), err } return hash, int(wc), nil }