func Encoder(c *rest.Context, data interface{}) error { var resp = new(struct { Code int `json:"code"` Status string `json:"status"` Success bool `json:"success"` Location string `json:"location,omitempty"` Error string `json:"error,omitempty"` Data interface{} `json:"data,omitempty"` *httpfs.FileInfo }) switch data := data.(type) { case error: resp.Error = data.Error() case *rest.RedirectURL: resp.Location = string(data.URL) case *httpfs.FileInfo: resp.FileInfo = data resp.Location = data.Name case *urlinfo.Info: resp.Data = data default: resp.Data = data } resp.Code = c.Status() resp.Status = http.StatusText(resp.Code) resp.Success = resp.Code < 400 c.SetContentType("application/json; charset=utf-8") enc := json.NewEncoder(c.Response) enc.SetIndent("", " ") return enc.Encode(resp) }
// Save сохраняет переданный в запросе файл в хранилище. func (fs *FileStore) Save(c *rest.Context) error { fileInfo, err := fs.Store.Create(c.Request.Body) // сохраняем в хранилище if err != nil { return err } // файл успешно сохранен var url = c.Request.URL if !strings.HasSuffix(url.Path, "/") { // добавляем к URL запроса "/" в конце, чтобы корректно обработать // объединение относительного пути url.Path += "/" } // объединяем текущий URL запроса с именем сохраненного файла url, _ = url.Parse(fileInfo.Name) urlStr := url.String() // полный URL для доступа к файлу fileInfo.Name = urlStr c.SetHeader("Location", urlStr) log.WithFields(log.Fields{ "url": urlStr, "mime": fileInfo.Mimetype, "size": fileInfo.Size, }).Debug("file saved") // отдаем ответ с информацией о файле c.SetStatus(http.StatusCreated) return c.Write(fileInfo) }
// URLInfo возвращает информацию об указанном в запросе URL. func URLInfo(c *rest.Context) error { urlStr := c.Param("url") if info := urlinfo.Get(urlStr); info != nil { return c.Write(info) } return c.Error(http.StatusBadRequest, fmt.Sprintf("bad urlinfo request %s", urlStr)) }
// Get отдает файл с аватаркой func (a *Avatars) Get(c *rest.Context) error { // получаем имя MX-сервера из пути mxname := c.Param("mx-name") // получаем идентификатор пользователя uid := c.Param("filename") // вычисляем полный путь к файлу с аватаркой avatar := filepath.Join(a.Path, mxname, uid) return c.ServeFile(avatar) }
// Authorize проверяет авторизацию пользователя и только в случае успешной // авторизации запускает обработчик запроса. func (a *Auth) Authorize(c *rest.Context) error { // получаем имя MX-сервера из пути mxname := c.Param("mx-name") // проверяем псевдонимы и делаем редирект на основное имя if name, ok := a.aliases[mxname]; ok { var url = strings.Replace(c.Request.URL.Path, mxname, name, 1) var status = http.StatusMovedPermanently var method = c.Request.Method if method != "GET" && method != "HEAD" { status = http.StatusPermanentRedirect } return c.Redirect(status, url) } // редирект для данного псевдонима не определен auth, ok := a.mxlist[mxname] // проверяем, что сервер с таким именем определен if !ok { return c.Error(http.StatusNotFound, fmt.Sprintf("mx %s not found", mxname)) } // проверяем авторизацию, переданную в запросе login, password, ok := c.BasicAuth() if !ok { realm := fmt.Sprintf("Basic realm=%q", mxname) c.SetHeader("WWW-Authenticate", realm) return rest.ErrUnauthorized } // проверяем авторизацию пользователя на сервере MX или в кеше ui, err := auth.Login(login, password) switch err := err.(type) { case nil: break case *mxlogin.Error: // неверная авторизация // if err.Code == 6 { // break // пользователь уже залогинен // } return c.Error(http.StatusForbidden, err.Error()) default: // проблемы с подключением к MX серверу return c.Error(http.StatusServiceUnavailable, err.Error()) } // сохраняем идентификатор пользователя c.SetData(userID, ui.ID) return nil }
// Get отдает файл из хранилища файлов по запросу HTTP. func (fs *FileStore) Get(c *rest.Context) (err error) { filename := c.Param("filename") // имя файла в хранилище return fs.Store.ServeFile(c.Response, c.Request, filename) // отдаем как есть }
// AvatarGenerator возвращает сгенерированную аватарку пользователя func AvatarGenerator(c *rest.Context) error { c.SetContentType("image/png") return c.Write(icon.Render([]byte(c.Param("id")))) }
// Save сохраняет аватарку пользователя в файле func (a *Avatars) Save(c *rest.Context) error { // получаем имя MX-сервера из пути mxname := c.Param("mx-name") // получаем идентификатор пользователя uid, ok := c.Data(userID).(uint64) if !ok { return c.Error(http.StatusInternalServerError, "bad user ID (JID)") } // создаем катало для сохранения аватарки fldr := filepath.Join(a.Path, mxname) if err := os.MkdirAll(fldr, 0700); err != nil { return c.Error(http.StatusInternalServerError, err.Error()) } // добавляем идентификатор пользователя, как имя файла id := strconv.FormatUint(uid, 10) avatar := filepath.Join(fldr, id) // создаем файл file, err := os.Create(avatar) if err != nil { return c.Error(http.StatusInternalServerError, err.Error()) } // копируем в файл содержимое запроса _, err = io.Copy(file, c.Request.Body) file.Close() if err != nil { return c.Error(http.StatusInternalServerError, err.Error()) } // сохраняем дату файла в списке fi, err := os.Stat(avatar) if err != nil { return c.Error(http.StatusInternalServerError, err.Error()) } a.mu.Lock() mxlist := a.list[mxname] if mxlist == nil { mxlist = make(map[uint64]time.Time) } mxlist[uid] = fi.ModTime() a.list[mxname] = mxlist a.mu.Unlock() // отдаем в заголовке путь к созданному файлу c.SetHeader("Location", path.Join(c.Request.URL.Path, id)) c.SetStatus(http.StatusCreated) return c.Write(nil) }
// List возвращает список сохраненных аватарок func (a *Avatars) List(c *rest.Context) error { a.mu.RLock() defer a.mu.RUnlock() return c.Write(a.list[c.Param("mx-name")]) }