package handlers import ( "encoding/json" "errors" "git.insit.tech/psa/rtsp_reader-writer/reader/internal/config" "github.com/golang-jwt/jwt/v5" "github.com/sirupsen/logrus" "log" "net/http" "os" "strconv" "strings" "time" ) // Структура HTTP-запроса на регистрацию пользователя type registerRequest struct { Email string `json:"email"` Name string `json:"name"` Password string `json:"password"` } // Структура HTTP-запроса на вход в аккаунт type loginRequest struct { Email string `json:"email"` Password string `json:"password"` } // Структура HTTP-ответа на вход в аккаунт // В ответе содержится JWT-токен авторизованного пользователя type loginResponse struct { AccessToken string `json:"access_token"` } var ( ErrBadCredentials = errors.New("email or password is incorrect") ) // Структура HTTP-ответа с информацией о пользователе type ProfileResponse struct { Email string `json:"email"` Name string `json:"name"` } type VideoRequest struct { Date string `json:"date"` StartTime string `json:"start_time"` EndTime string `json:"end_time"` } type ListVodsResponse struct { EstimatedCount int `json:"estimated_count"` // Estimated total number of records for the query. Vods []string `json:"vods"` // List of vods. Files map[string][]string `json:"files"` // List files and folders of a specific VOD location. } type ConfigVodsResponse struct { Prefix string `json:"prefix"` // The unique name of VOD location. AutoMbr bool `json:"auto_mbr"` // Turns on automatic creation of a multi-bitrate HLS playlist from several files with different bitrates. Disabled bool `json:"disabled"` // Whether this VOD location is disabled. Protocols struct { // Configuraton of play protocols. Hls bool `json:"hls"` // Whether to allow or deny an HLS stream playback. Cmaf bool `json:"cmaf"` // Whether to allow or deny an LL-HLS stream playback. Dash bool `json:"dash"` // Whether to allow or deny a DASH stream playback. Player bool `json:"player"` // Whether to allow or deny playback in embed.html. Mss bool `json:"mss"` // Whether to allow or deny an MSS stream playback. Rtmp bool `json:"rtmp"` // Whether to allow or deny an RTMP stream playback. Rtsp bool `json:"rtsp"` // Whether to allow or deny an RTSP stream playback. M4F bool `json:"m4f"` // Whether to allow or deny an M4F stream playback. M4S bool `json:"m4s"` // Whether to allow or deny an M4S stream playback. Mseld bool `json:"mseld"` // Whether to allow or deny an MSE-LD stream playback. Tshttp bool `json:"tshttp"` // Whether to allow or deny an MPEG-TS stream playback over HTTP(S). Webrtc bool `json:"webrtc"` // Whether to allow or deny an WebRTC stream playback. Srt bool `json:"srt"` // Whether to allow or deny an SRT stream playback. Shoutcast bool `json:"shoutcast"` // Whether to allow or deny a SHOUTcast/Icecast stream playback. Mp4 bool `json:"mp4"` // Whether to allow or deny an MP4 file download over HTTP(S). Jpeg bool `json:"jpeg"` // Whether to allow or deny delivering JPEG thumbnails over HTTP(S). Api bool `json:"api"` // Whether to allow or deny API requests. } `json:"protocols"` SegmentDuration int `json:"segment_duration"` // The time, in seconds, of the segment duration. Used for the protocols like HLS or DASH. AddAudioOnly bool `json:"add_audio_only"` // Whether to add an audio-only version of an HLS stream. Used to create App Store compliant HLS streams to deliver the content to Apple iOS devices. Add audio-only HLS playlist to variant MBR playlist for iOS compliant streaming. Provider string `json:"provider"` // Human-readable name of the content provider. Applicable to MPEG-TS. } type SingleVodsResponse struct { Name string `json:"name"` Prefix string `json:"prefix"` Url string `json:"url"` Folder string `json:"folder"` Bytes int64 `json:"bytes"` MediaInfo MediaSingleVodsResponse `json:"media_info"` } type MediaSingleVodsResponse struct { Tracks map[string]string `json:"tracks"` Duration int `json:"duration"` Provider string `json:"provider"` Title string `json:"title"` } // Обработчик HTTP-запросов на регистрацию пользователя func Register(w http.ResponseWriter, r *http.Request) { regReq := registerRequest{} if err := json.NewDecoder(r.Body).Decode(®Req); err != nil { w.WriteHeader(http.StatusBadRequest) return } // Проверяем, что пользователь с таким email еще не зарегистрирован if _, exists := config.Storage.Users[regReq.Email]; exists { w.WriteHeader(http.StatusBadRequest) return } // Сохраняем в память нового зарегистрированного пользователя config.Storage.Users[regReq.Email] = config.User{ Email: regReq.Email, Name: regReq.Name, Password: regReq.Password, } w.WriteHeader(http.StatusCreated) } // Обработчик HTTP-запросов на вход в аккаунт func Login(w http.ResponseWriter, r *http.Request) { regReq := loginRequest{} if err := json.NewDecoder(r.Body).Decode(®Req); err != nil { w.WriteHeader(http.StatusBadRequest) return } // Ищем пользователя в памяти приложения по электронной почте user, exists := config.Storage.Users[regReq.Email] // Если пользователь не найден, возвращаем ошибку if !exists { w.WriteHeader(http.StatusBadRequest) return } // Если пользователь найден, но у него другой пароль, возвращаем ошибку if user.Password != regReq.Password { w.WriteHeader(http.StatusBadRequest) return } // Генерируем JWT-токен для пользователя, // который он будет использовать в будущих HTTP-запросах // Генерируем полезные данные, которые будут храниться в токене payload := jwt.MapClaims{ "sub": user.Email, "exp": time.Now().Add(time.Hour * 24).Unix(), } // Создаем новый JWT-токен и подписываем его по алгоритму HS256 token := jwt.NewWithClaims(jwt.SigningMethodHS256, payload) t, err := token.SignedString(config.JwtSecretKey) if err != nil { logrus.WithError(err).Error("JWT token signing") w.WriteHeader(http.StatusInternalServerError) return } res, err := json.Marshal(loginResponse{AccessToken: t}) if err != nil { w.WriteHeader(http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json") if _, err = w.Write(res); err != nil { w.WriteHeader(http.StatusInternalServerError) return } //w.WriteHeader(http.StatusOK) } // Inactive5Minutes checks if a folder has files that was not created or modified for last 5 minutes. func Inactive5Minutes(entries []os.DirEntry) (bool, error) { threshold := time.Now().Add(-5 * time.Minute) disabled := true for _, entry := range entries { info, err := entry.Info() if err != nil { return true, errors.New("Info error: " + err.Error()) } if info.ModTime().After(threshold) { disabled = false return disabled, nil } } return true, nil } // RecDurationMilliseconds calculates the difference between first existing rec file and the last one in milliseconds. func RecDurationMilliseconds(entries []os.DirEntry) (int, error) { // Find last file and first file; get timestamps. lastFile := entries[len(entries)-1].Name() lastTime := strings.Split(lastFile, "_")[0] firstFile := entries[0].Name() firstTime := strings.Split(firstFile, "_")[0] // Convert string timestamps to int. lastTimeInt, err := strconv.Atoi(lastTime) if err != nil { return 0, errors.New("convert last time error: " + err.Error()) } firstTimeInt, err := strconv.Atoi(firstTime) if err != nil { return 0, errors.New("convert first time error: " + err.Error()) } // Calculate the difference. difference := lastTimeInt - firstTimeInt return difference * 1000, nil } // ListVodsHandlerLogic implements main logic of handler ListVodsHandler. func ListVodsHandlerLogic() (ListVodsResponse, error) { // Read directory. entries, err := os.ReadDir(config.DirData) if err != nil { return ListVodsResponse{}, err } // Filter only folders. var dirs []string for _, entry := range entries { if entry.IsDir() { dirs = append(dirs, entry.Name()) } } // Prepare the Response. VodsRes := ListVodsResponse{ EstimatedCount: len(dirs), Vods: dirs, } return VodsRes, nil } // ListVodsHandler returns the list of VOD locations. // // This method allows to get the list of all VOD locations. VOD location is a virtual filepath used to place files for // VOD (Video on Demand) broadcasting. func ListVodsHandler() http.Handler { log.Println("before return ListVodsHandler") return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { log.Println("hello fron ListVodsHandler") // Prepare the Response. VodsRes, err := ListVodsHandlerLogic() if err != nil { w.WriteHeader(http.StatusInternalServerError) return } // Write header and code response. w.Header().Set("Content-Type", "application/json") if err = json.NewEncoder(w).Encode(VodsRes); err != nil { w.WriteHeader(http.StatusInternalServerError) return } log.Println("hello fron ListVodsHandlerLogic: StatusOK") }) }