// 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) }
func main() { log.SetLevel(log.DebugLevel) log.SetFlags(0) // выводим информацию о версии сборки log.WithFields(log.Fields{ "version": version, "date": date, "build": build, "name": appName, }).Info("starting service") // разбираем параметры запуска приложения config := flag.String("config", appName+".json", "configuration `filename`") address := flag.String("address", host, "server address and `port`") flag.Parse() // загружаем конфигурацию сервиса log.WithField("file", *config).Info("loading service config") service, err := LoadConfig(*config) if err != nil { log.WithError(err).Error("loading config error") os.Exit(1) } // выводим список поддерживаемых MX-серверов mxnames := service.Auth.Names() if len(mxnames) == 0 { log.Error("no MX servers configured") os.Exit(1) } log.WithField("names", strings.Join(mxnames, ", ")). Info("supported MX servers") // выводим информацию о кеше авторизации log.WithField("lifeTime", mxlogin.CacheLifetime). Info("authorization cache") // удаляем устаревшие файлы раз в сутки cleanInterval := time.Hour * 24 // выводим информацию о хранилище файлов log.WithFields(log.Fields{ "path": service.FileStore.Store.Path(), "lifeTime": service.FileStore.Lifetime, "cleanInterval": cleanInterval, }).Info("file store") // запускаем процесс автоматического удаления старых файлов go func() { for { time.Sleep(cleanInterval) err = service.FileStore.Clean() if err != nil { log.WithField("service", "filestore"). WithError(err).Debug("file store clean error") } } }() // инициализируем мультиплексор HTTP-запросов mux := &rest.ServeMux{ Headers: map[string]string{ "Server": "MXStore/2.0", "X-API-Version": "1.1", "X-Service-Version": version, }, Logger: log.WithField("service", "http"), Encoder: Encoder, } // обработчики файлов хранилища и аватарок mux.Handles(rest.Paths{ "/mx/:mx-name/store": { // переходим на основное имя MX сервера, если используется синоним // возвращает пустой ответ, если имя сервера и так основное "GET": nil, // сохраняем в хранилище новый файл "POST": service.FileStore.Save, }, // отдаем файл по запросу "/mx/:mx-name/store/*filename": { "GET": service.FileStore.Get, }, "/mx/:mx-name/avatar": { // переходим на основное имя MX сервера, если используется синоним // возвращаем список идентификаторов пользователей и аватарок "GET": service.Avatars.List, // сохраняем в хранилище новый файл "POST": service.Avatars.Save, }, // отдаем файл с аватаркой по запросу "/mx/:mx-name/avatar/*filename": { "GET": service.Avatars.Get, }, }, // для всех запросов требуется авторизация service.Auth.Authorize, ) // добавляем обработчик отдачи документации mux.Handle("GET", "/", rest.Redirect("https://www.connector73.com/en#softphone")) // запускаем сервис с ответом об информации о ссылке mux.Handle("GET", "/urlinfo/*url", URLInfo) // генератор аватарок mux.Handle("GET", "/avatar/:id", AvatarGenerator) // инициализируем HTTP-сервер server := &http.Server{ Addr: *address, Handler: mux, ReadTimeout: time.Second * 60, WriteTimeout: time.Second * 120, } // для защищенного соединения проделываем дополнительные настройки host, port, err := net.SplitHostPort(*address) if err != nil { log.WithError(err).Error("bad server address") os.Exit(2) } if port == "https" || port == "443" { if host != "localhost" && host != "127.0.0.1" { manager := autocert.Manager{ Prompt: autocert.AcceptTOS, HostPolicy: autocert.HostWhitelist(host), Email: "*****@*****.**", Cache: autocert.DirCache("letsEncript.cache"), } server.TLSConfig = &tls.Config{ GetCertificate: manager.GetCertificate, } } else { // исключительно для отладки cert, err := tls.X509KeyPair(LocalhostCert, LocalhostKey) if err != nil { panic(fmt.Sprintf("local certificates error: %v", err)) } server.TLSConfig = &tls.Config{ Certificates: []tls.Certificate{cert}, } } // запускаем автоматический переход для HTTP на HTTPS go func() { log.Info("starting http to https redirect server") err := http.ListenAndServe(":http", http.HandlerFunc( func(w http.ResponseWriter, r *http.Request) { http.Redirect(w, r, "https://"+r.Host+r.URL.String(), http.StatusMovedPermanently) })) if err != nil { log.WithError(err).Warning("http redirect server error") } }() // запускаем основной сервер go func() { log.WithFields(log.Fields{ "address": server.Addr, "host": host, }).Info("starting https server") err = server.ListenAndServeTLS("", "") // // корректно закрываем сервисы по окончании работы // service.Close() log.WithError(err).Warning("https server stoped") os.Exit(3) }() } else { // не защищенный HTTP сервер go func() { log.WithField("address", *address).Info("starting service") err = server.ListenAndServe() // service.Close() log.WithError(err).Warning("http server stoped") os.Exit(3) }() } // инициализируем поддержку системных сигналов и ждем, когда он случится monitorSignals(os.Interrupt, os.Kill) // service.Close() log.Info("service stoped") }