Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- package main
- import (
- "crypto/md5"
- "crypto/tls"
- "encoding/json"
- "fmt"
- "html/template"
- "io"
- "log"
- "mime"
- "net/http"
- "net/url"
- "os"
- "os/exec"
- "strconv"
- "strings"
- "time"
- "github.com/go-chi/chi"
- basicauth "gopkg.in/99designs/basicauth-go.v0"
- "gopkg.in/mgo.v2"
- "gopkg.in/mgo.v2/bson"
- )
- const tpl1 = `
- <html>
- <head>
- <meta name="viewport" content="width=device-width, initial-scale=1">
- <title>Upload file</title>
- <link rel="stylesheet" href="https://gitcdn.link/repo/Chalarangelo/mini.css/master/dist/mini-default.min.css">
- <link rel="stylesheet" href="https://cdn.rawgit.com/Chalarangelo/mini.css/v2.3.4/dist/mini-default.min.css">
- <style>
- .input-group [type="checkbox"]+label {
- margin-left: 1.5rem;
- }
- </style>
- </head>
- <body>
- <div class="container">
- <div class="row">
- <div class="col-sm">
- <h2>Upload</h2>
- </div>
- </div>
- <div class="row">
- <form enctype="multipart/form-data" action="/" method="post" class="col-sm">
- <fieldset>
- <legend>File Upload</legend>
- <div class="input-group vertical">
- <input type="file" name="uploadfile" id="uploadfile" />
- <label for="uploadfile" class="button">uploadfile</label>
- </div>
- <div class="input-group vertical">
- <label for="pngqlt">pngqlt</label>
- <input type="text" name="pngqlt" id="pngqlt" value="60" />
- <div class="input-group vertical">
- <label for="jpgqlt">jpgqlt</label>
- <input type="text" name="jpgqlt" id="jpgqlt" value="75" />
- </div>
- <div class="input-group vertical">
- <input type="checkbox" name="keep" id="keep" value="1" checked />
- <label for="keep">keep</label>
- </div>
- </fieldset>
- <input type="submit" class="primary" value="Upload" />
- </form>
- </div>
- </div>
- </body>
- </html>
- `
- const tpl2 = `
- <html>
- <head>
- <meta name="viewport" content="width=device-width, initial-scale=1">
- <title>Upload file</title>
- <link rel="stylesheet" href="https://gitcdn.link/repo/Chalarangelo/mini.css/master/dist/mini-default.min.css">
- <link rel="stylesheet" href="https://cdn.rawgit.com/Chalarangelo/mini.css/v2.3.4/dist/mini-default.min.css">
- <style>
- .input-group [type="checkbox"]+label {
- margin-left: 1.5rem;
- }
- </style>
- </head>
- <body>
- <div class="container">
- <div class="row">
- <div class="col-sm">
- <h2>Upload</h2>
- </div>
- </div>
- <div class="row">
- <form action="/url" method="post" class="col-sm">
- <fieldset>
- <legend>File Upload</legend>
- <div class="input-group vertical">
- <label for="uploadfile">uploadfile</label>
- <input type="text" name="uploadfile" id="uploadfile" />
- </div>
- <div class="input-group vertical">
- <label for="pngqlt">pngqlt</label>
- <input type="text" name="pngqlt" id="pngqlt" value="60" />
- </div>
- <div class="input-group vertical">
- <label for="jpgqlt">jpgqlt</label>
- <input type="text" name="jpgqlt" id="jpgqlt" value="75" />
- </div>
- <div class="input-group vertical">
- <input type="checkbox" name="keep" id="keep" value="1" checked />
- <label for="keep">keep</label>
- </div>
- </fieldset>
- <input type="submit" class="primary" value="Upload" />
- </form>
- </div>
- </div>
- </body>
- </html>
- `
- const tpl3 = `
- <html>
- <head>
- <meta name="viewport" content="width=device-width, initial-scale=1">
- <title>Upload file</title>
- <link rel="stylesheet" href="https://gitcdn.link/repo/Chalarangelo/mini.css/master/dist/mini-default.min.css">
- <link rel="stylesheet" href="https://cdn.rawgit.com/Chalarangelo/mini.css/v2.3.4/dist/mini-default.min.css">
- <style>
- a {
- margin-right: 1rem;
- }
- a.once {
- margin-right: 3.5rem;
- }
- a.once::after {
- background-color: #e53935;
- border-radius: 0.25rem;
- color: white;
- content: "once";
- font-size: 0.7rem;
- line-height: 1;
- margin-left: 0.25rem;
- margin-top: -0.25rem;
- padding: 0.15rem 0;
- position: absolute;
- text-align: center;
- width: 2.25rem;
- }
- a:last-child {
- margin-right: 0;
- }
- </style>
- </head>
- <body>
- <div class="container">
- <div class="row">
- <div class="col-sm">
- <h2>Store</h2>
- </div>
- </div>
- <div class="row">
- <div class="col-sm">
- <p>
- {{range .}}
- <a
- href="/store/{{.ID.Hex}}"
- {{if eq .Keep 0}}
- class="once"
- {{end}}
- >{{.Name}}</a>
- {{end}}
- </p>
- </div>
- </div>
- </div>
- <script type="text/javascript">
- document
- .querySelectorAll(".once")
- .forEach(function (elm) {
- elm
- .addEventListener(
- "click",
- function (evt) {
- evt.target.remove();
- }
- );
- });
- </script>
- </body>
- </html>
- `
- // Entry is a file entry
- type Entry struct {
- ID bson.ObjectId `bson:"_id,omitempty"`
- Name string `bson:"name"`
- Path string `bson:"path"`
- ContentType string `bson:"content_type"`
- InSize int64 `bson:"in_size"`
- OutSize int64 `bson:"out_size"`
- User string `bson:"user"`
- Keep int8 `bson:"keep"`
- Timestamp time.Time `bson:"timestamp"`
- }
- type User struct {
- ID bson.ObjectId `bson:"_id,omitempty"`
- name string `bson:"name"`
- }
- func createAuthMiddleware(realm string, s *mgo.Session) func(http.Handler) http.Handler {
- return func(next http.Handler) http.Handler {
- return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- name, password, ok := r.BasicAuth()
- if !ok {
- unauthorized(w, realm)
- return
- }
- session := s.Copy()
- defer session.Close()
- u := User{}
- session.DB("store").C("users").Find(bson.M{"name": name}).One(&u)
- if !userFound {
- unauthorized(w, realm)
- return
- }
- for _, validPassword := range validPasswords {
- if password == validPassword {
- next.ServeHTTP(w, r)
- return
- }
- }
- unauthorized(w, realm)
- })
- }
- }
- func unauthorized(w http.ResponseWriter, realm string) {
- w.Header().Add("WWW-Authenticate", fmt.Sprintf(`Basic realm="%s"`, realm))
- w.WriteHeader(http.StatusUnauthorized)
- }
- func calcPath(fn string) string {
- crutime := time.Now().Unix()
- h := md5.New()
- io.WriteString(h, fmt.Sprintf("%s + %s", strconv.FormatInt(crutime, 20), fn))
- hash := h.Sum(nil)
- return fmt.Sprintf("./store/%x-%s", hash, fn)
- }
- func optImg(pt string, ct string, pngqlt int, jpgqlt int) error {
- if ct == "image/png" {
- cmd := exec.Command("pngquant", "--quality", fmt.Sprintf("%s-%s", strconv.Itoa(pngqlt), strconv.Itoa(pngqlt)), "--ext", ".png", "--force", pt)
- if err := cmd.Run(); err != nil {
- return err
- }
- }
- if ct == "image/jpeg" {
- cmd := exec.Command("jpegoptim", "-m", strconv.Itoa(jpgqlt), pt)
- if err := cmd.Run(); err != nil {
- return err
- }
- }
- return nil
- }
- func uploadGet(w http.ResponseWriter, r *http.Request) {
- t, _ := template.New("upload").Parse(tpl1)
- t.Execute(w, nil)
- }
- func uploadPost(s *mgo.Session) func(w http.ResponseWriter, r *http.Request) {
- return func(w http.ResponseWriter, r *http.Request) {
- r.ParseMultipartForm(32 << 20)
- jpgqlt, err := strconv.Atoi(r.Form.Get("jpgqlt"))
- if err != nil || jpgqlt < 0 || jpgqlt > 100 {
- jpgqlt = 75
- }
- pngqlt, err := strconv.Atoi(r.Form.Get("pngqlt"))
- if err != nil || pngqlt < 0 || pngqlt > 100 {
- pngqlt = 60
- }
- keep, err := strconv.ParseInt(r.Form.Get("keep"), 10, 8)
- if err != nil {
- keep = 0
- }
- f, fh, err := r.FormFile("uploadfile")
- if err != nil {
- errStr := fmt.Sprintf("FormFile: %s", err.Error())
- log.Println(errStr)
- http.Error(w, errStr, 400)
- return
- }
- defer f.Close()
- pt := calcPath(fh.Filename)
- fo, err := os.OpenFile(pt, os.O_RDWR|os.O_CREATE, 0666)
- if err != nil {
- errStr := fmt.Sprintf("OpenFile: %s", err.Error())
- log.Println(errStr)
- http.Error(w, errStr, 500)
- return
- }
- defer fo.Close()
- io.Copy(fo, f)
- err = optImg(fo.Name(), fh.Header.Get("Content-Type"), pngqlt, jpgqlt)
- if err != nil {
- errStr := fmt.Sprintf("OptImg %s: %s", fh.Header.Get("Content-Type"), err.Error())
- log.Println(errStr)
- http.Error(w, errStr, 400)
- os.Remove(fo.Name())
- return
- }
- fi, _ := os.Stat(fo.Name())
- un, _, _ := r.BasicAuth()
- sc := s.Copy()
- defer sc.Close()
- id := bson.NewObjectId()
- e := &Entry{
- ID: id,
- Name: fh.Filename,
- Path: fo.Name(),
- ContentType: fh.Header.Get("Content-Type"),
- InSize: r.ContentLength,
- OutSize: fi.Size(),
- User: un,
- Keep: int8(keep),
- Timestamp: time.Now(),
- }
- err = sc.DB("store").C("entries").Insert(e)
- if err != nil {
- errStr := fmt.Sprintf("DbInsert: %s", err.Error())
- log.Println(errStr)
- http.Error(w, errStr, 500)
- }
- respBody, err := json.MarshalIndent(e, "", " ")
- if err != nil {
- errStr := fmt.Sprintf("MarshalIndent: %s", err.Error())
- log.Println(errStr)
- http.Error(w, errStr, 500)
- }
- w.Header().Set("Content-Type", "application/json; charset=utf-8")
- w.Write(respBody)
- }
- }
- func urlGet(w http.ResponseWriter, r *http.Request) {
- t, _ := template.New("upload").Parse(tpl2)
- t.Execute(w, nil)
- }
- func urlPost(s *mgo.Session) func(w http.ResponseWriter, r *http.Request) {
- return func(w http.ResponseWriter, r *http.Request) {
- r.ParseForm()
- jpgqlt, err := strconv.Atoi(r.Form.Get("jpgqlt"))
- if err != nil || jpgqlt < 0 || jpgqlt > 100 {
- jpgqlt = 75
- }
- pngqlt, err := strconv.Atoi(r.Form.Get("pngqlt"))
- if err != nil || pngqlt < 0 || pngqlt > 100 {
- pngqlt = 60
- }
- keep, err := strconv.ParseInt(r.Form.Get("keep"), 10, 8)
- if err != nil {
- keep = 0
- }
- uri := r.Form.Get("uploadfile")
- if _, err = url.ParseRequestURI(uri); err != nil {
- errStr := fmt.Sprintf("ParseRequestURI: %s", err.Error())
- log.Println(errStr)
- http.Error(w, errStr, 400)
- return
- }
- tr := &http.Transport{
- TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
- }
- client := &http.Client{Transport: tr}
- resp, err := client.Get(uri)
- if err != nil {
- errStr := fmt.Sprintf("ClientGet: %s", err.Error())
- log.Println(errStr)
- http.Error(w, errStr, 400)
- return
- }
- defer resp.Body.Close()
- ct := resp.Header.Get("Content-Type")
- sl := strings.Split(resp.Request.URL.Path, "/")
- sl = strings.Split(sl[len(sl)-1], ".")
- fn := strings.Join(sl[0:len(sl)-1], ".")
- if len(fn) == 0 {
- fn = "untitled"
- }
- var ex string
- if len(sl) > 1 {
- ex = fmt.Sprintf(".%s", sl[len(sl)-1])
- }
- if len(ex) == 0 {
- exs, _ := mime.ExtensionsByType(ct)
- if len(exs) > 0 {
- ex = exs[0]
- }
- }
- fn += ex
- pt := calcPath(fn)
- fo, err := os.OpenFile(pt, os.O_RDWR|os.O_CREATE, 0666)
- if err != nil {
- errStr := fmt.Sprintf("OpenFile: %s", err.Error())
- log.Println(errStr)
- http.Error(w, errStr, 500)
- return
- }
- defer fo.Close()
- io.Copy(fo, resp.Body)
- err = optImg(fo.Name(), ct, pngqlt, jpgqlt)
- if err != nil {
- errStr := fmt.Sprintf("OptImg %s: %s", ct, err.Error())
- log.Println(errStr)
- http.Error(w, errStr, 400)
- os.Remove(fo.Name())
- return
- }
- fi, _ := os.Stat(fo.Name())
- un, _, _ := r.BasicAuth()
- sc := s.Copy()
- defer sc.Close()
- id := bson.NewObjectId()
- e := &Entry{
- ID: id,
- Name: fn,
- Path: fo.Name(),
- ContentType: ct,
- InSize: resp.ContentLength,
- OutSize: fi.Size(),
- User: un,
- Keep: int8(keep),
- Timestamp: time.Now(),
- }
- err = sc.DB("store").C("entries").Insert(e)
- if err != nil {
- errStr := fmt.Sprintf("DbInsert: %s", err.Error())
- log.Println(errStr)
- http.Error(w, errStr, 500)
- return
- }
- respBody, err := json.MarshalIndent(e, "", " ")
- if err != nil {
- errStr := fmt.Sprintf("MarshalIndent: %s", err.Error())
- log.Println(errStr)
- http.Error(w, errStr, 500)
- return
- }
- w.Header().Set("Content-Type", "application/json; charset=utf-8")
- w.Write(respBody)
- }
- }
- func serveFiles(s *mgo.Session) func(w http.ResponseWriter, r *http.Request) {
- return func(w http.ResponseWriter, r *http.Request) {
- session := s.Copy()
- defer session.Close()
- id := chi.URLParam(r, "id")
- if ch := bson.IsObjectIdHex(id); !ch {
- errStr := fmt.Sprintf("IsObjectIdHex: %t", ch)
- log.Println(errStr)
- http.Error(w, errStr, 400)
- return
- }
- e := Entry{}
- err := session.DB("store").C("entries").Find(bson.M{"_id": bson.ObjectIdHex(id)}).One(&e)
- if err != nil {
- errStr := fmt.Sprintf("DbFindOne: %s", err.Error())
- log.Println(errStr)
- http.Error(w, errStr, 404)
- return
- }
- f, err := os.OpenFile(e.Path, os.O_RDONLY, 0666)
- if err != nil {
- errStr := fmt.Sprintf("OpenFile: %s", err.Error())
- log.Println(errStr)
- http.Error(w, errStr, 404)
- err := session.DB("store").C("entries").RemoveId(e.ID)
- if err != nil {
- errStr := fmt.Sprintf("DbRemoveId: %s", err.Error())
- log.Println(errStr)
- http.Error(w, errStr, 500)
- return
- }
- return
- }
- defer f.Close()
- w.Header().Set("Content-Disposition", "attachment; filename="+e.Name)
- w.Header().Set("Content-Type", e.ContentType)
- io.Copy(w, f)
- if e.Keep == 0 {
- os.Remove(e.Path)
- err := session.DB("store").C("entries").RemoveId(e.ID)
- if err != nil {
- errStr := fmt.Sprintf("DbRemoveId: %s", err.Error())
- log.Println(errStr)
- http.Error(w, errStr, 500)
- return
- }
- }
- }
- }
- func showFiles(s *mgo.Session) func(w http.ResponseWriter, r *http.Request) {
- return func(w http.ResponseWriter, r *http.Request) {
- session := s.Copy()
- defer session.Close()
- un, _, _ := r.BasicAuth()
- var es []Entry
- err := session.DB("store").C("entries").Find(bson.M{"user": un}).Sort("-timestamp").Limit(500).All(&es)
- if err != nil {
- errStr := fmt.Sprintf("DbFindAll: %s", err.Error())
- log.Println(errStr)
- http.Error(w, errStr, 500)
- return
- }
- t, _ := template.New("upload").Parse(tpl3)
- err = t.Execute(w, es)
- if err != nil {
- errStr := fmt.Sprintf("TemplateExecute: %s", err.Error())
- log.Println(errStr)
- http.Error(w, errStr, 500)
- return
- }
- }
- }
- func cleanGet(s *mgo.Session) func(w http.ResponseWriter, r *http.Request) {
- return func(w http.ResponseWriter, r *http.Request) {
- session := s.Copy()
- defer session.Close()
- var dtk int
- dtk, _ = strconv.Atoi(os.Getenv("DAYS_TO_KEEP"))
- var es []Entry
- err := session.DB("store").C("entries").Find(bson.M{"timestamp": bson.M{"$lt": time.Now().AddDate(0, 0, -dtk)}}).All(&es)
- if err != nil {
- errStr := fmt.Sprintf("DbFindAll: %s", err.Error())
- log.Println(errStr)
- http.Error(w, errStr, 400)
- return
- }
- for _, e := range es {
- os.Remove(e.Path)
- err := session.DB("store").C("entries").RemoveId(e.ID)
- if err != nil {
- errStr := fmt.Sprintf("DbRemoveId: %s", err.Error())
- log.Println(errStr)
- http.Error(w, errStr, 400)
- return
- }
- }
- resStr := fmt.Sprintf("Cleaned: %d", len(es))
- io.WriteString(w, resStr)
- log.Println(resStr)
- }
- }
- func main() {
- mgoAddr := strings.Join([]string{os.Getenv("DB_PORT_27017_TCP_ADDR"), os.Getenv("DB_PORT_27017_TCP_PORT")}, ":")
- session, err := mgo.Dial(mgoAddr)
- if err != nil {
- log.Fatal(err)
- }
- defer session.Close()
- session.SetMode(mgo.Monotonic, true)
- r := chi.NewRouter()
- authMiddleware := basicauth.NewFromEnv("MyRealm", "AUTH_")
- r.Route("/", func(r chi.Router) {
- r.Use(authMiddleware)
- r.Get("/", uploadGet)
- r.Post("/", uploadPost(session))
- })
- r.Route("/url", func(r chi.Router) {
- r.Use(authMiddleware)
- r.Get("/", urlGet)
- r.Post("/", urlPost(session))
- })
- r.Route("/store", func(r chi.Router) {
- r.With(authMiddleware).Get("/", showFiles(session))
- r.Get("/{id}", serveFiles(session))
- })
- r.Route("/clean", func(r chi.Router) {
- r.Get("/", cleanGet(session))
- })
- servAddr := strings.Join([]string{os.Getenv("HOST"), os.Getenv("PORT")}, ":")
- err = http.ListenAndServe(servAddr, r)
- if err != nil {
- log.Fatal("ListenAndServe: ", err)
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement