// // tank // // Import and Playlist Daemon for autoradio project // // // Copyright (C) 2017-2019 Christian Pointner <equinox@helsinki.at> // // This file is part of tank. // // tank is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // any later version. // // tank is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with tank. If not, see <http://www.gnu.org/licenses/>. // package v1 import ( "encoding/json" "net/http" "github.com/gin-gonic/gin" "gitlab.servus.at/autoradio/tank/importer" "gitlab.servus.at/autoradio/tank/store" ) func (api *API) ListFilesOfShow(c *gin.Context) { showID := c.Param("show-id") if authorized, _ := authorizeRequest(c, showID); !authorized { return } offset, limit, ok := getPaginationParameter(c) if !ok { return } files, err := api.store.ListFiles(showID, offset, limit) if err != nil { sendError(c, err) return } c.JSON(http.StatusOK, FilesListing{files}) } func (api *API) CreateFileForShow(c *gin.Context) { showID := c.Param("show-id") authorized, sess := authorizeRequest(c, showID) if !authorized { return } request := &FileCreateRequest{} err := json.NewDecoder(c.Request.Body).Decode(request) if err != nil { c.JSON(http.StatusBadRequest, ErrorResponse{Error: "error decoding request: " + err.Error()}) return } if request.SourceURI == "" { c.JSON(http.StatusBadRequest, ErrorResponse{Error: "source-uri is mandatory"}) return } srcURI, err := api.importer.ParseAndVerifySourceURI(request.SourceURI) if err != nil { c.JSON(http.StatusBadRequest, ErrorResponse{Error: "source-uri is invalid: " + err.Error()}) return } waitFor := c.Query("wait-for") switch waitFor { case "running": case "done": case "": default: c.JSON(http.StatusBadRequest, ErrorResponse{Error: "unable to wait for unknown state: " + waitFor}) return } file := &store.File{} file.Source.URI = request.SourceURI if file, err = api.store.CreateFile(showID, *file); err != nil { sendError(c, err) return } // From here on, in case of an error we should either delete the newly created // file or return the file object as ErrorResponse.Detail so the API user can deal with the problem. job, err := api.importer.CreateJob(showID, file.ID, srcURI, sess.Username, c.Query("ref-id")) if err != nil { goto create_file_response } if err = job.StartWithTimeout(0); err != nil { goto create_file_response } switch waitFor { case "running": <-job.Running() case "done": <-job.Done() } create_file_response: // GetFile only fails if the file does not exist or there is // a problem with the database connection. The former probably // means that the file got deleted by somebody else... all other // errors most likely mean that the file still exists. In this // case it's better to return the file so the API user can use // the file ID for further inspection. getFile, getErr := api.store.GetFile(showID, file.ID) if getErr == store.ErrNotFound { sendError(c, ErrFileVanished) return } // don't mask the original error if err == nil { err = getErr } if getFile != nil { file = getFile } if err == nil { c.JSON(http.StatusCreated, file) return } status, errResp := statusCodeFromError(err) errResp.Detail = file c.JSON(status, errResp) } func (api *API) ReadFileOfShow(c *gin.Context) { showID := c.Param("show-id") if authorized, _ := authorizeRequest(c, showID); !authorized { return } id, err := idFromString(c.Param("file-id")) if err != nil { c.JSON(http.StatusBadRequest, ErrorResponse{Error: "invalid file-id: " + err.Error()}) return } file, err := api.store.GetFile(showID, id) if err != nil { sendError(c, err) return } c.JSON(http.StatusOK, file) } func (api *API) PatchFileOfShow(c *gin.Context) { showID := c.Param("show-id") if authorized, _ := authorizeRequest(c, showID); !authorized { return } id, err := idFromString(c.Param("file-id")) if err != nil { c.JSON(http.StatusBadRequest, ErrorResponse{Error: "invalid file-id: " + err.Error()}) return } data := make(map[string]string) if err = json.NewDecoder(c.Request.Body).Decode(&data); err != nil { c.JSON(http.StatusBadRequest, ErrorResponse{Error: "error decoding request: " + err.Error()}) return } file, err := api.store.UpdateFileMetadata(showID, id, data) if err != nil { sendError(c, err) return } c.JSON(http.StatusOK, file) } func (api *API) DeleteFileOfShow(c *gin.Context) { showID := c.Param("show-id") if authorized, _ := authorizeRequest(c, showID); !authorized { return } id, err := idFromString(c.Param("file-id")) if err != nil { c.JSON(http.StatusBadRequest, ErrorResponse{Error: "invalid file-id: " + err.Error()}) return } if job, err := api.importer.GetJob(showID, id); err != importer.ErrNotFound { job.Cancel() } if err = api.store.DeleteFile(showID, id); err != nil { sendError(c, err) return } c.JSON(http.StatusNoContent, nil) } func (api *API) ReadUsageOfFile(c *gin.Context) { showID := c.Param("show-id") if authorized, _ := authorizeRequest(c, showID); !authorized { return } id, err := idFromString(c.Param("file-id")) if err != nil { c.JSON(http.StatusBadRequest, ErrorResponse{Error: "invalid file-id: " + err.Error()}) return } result := FileUsageListing{} if result.Usage.Playlists, err = api.store.GetFileUsage(showID, id); err != nil { sendError(c, err) return } c.JSON(http.StatusOK, result) } func (api *API) ReadLogsOfFile(c *gin.Context) { showID := c.Param("show-id") if authorized, _ := authorizeRequest(c, showID); !authorized { return } id, err := idFromString(c.Param("file-id")) if err != nil { c.JSON(http.StatusBadRequest, ErrorResponse{Error: "invalid file-id: " + err.Error()}) return } result := FileImportLogs{} if result.Logs, err = api.store.GetImportLogs(showID, id); err != nil { sendError(c, err) return } c.JSON(http.StatusOK, result) }