From c6c6f0233dc54577a6725818b21e50820fa56884 Mon Sep 17 00:00:00 2001 From: Gabe Kangas Date: Sun, 12 Sep 2021 00:18:15 -0700 Subject: [PATCH] Expanded linting + fix warnings (#1396) * Expand the linters and types of warnings to improve consistency and safety * Fail lint workflow if there are errors * golint has been replaced by revive * Hand-pick some of the default exclude list * Ignore error when trying to delete preview gif * Ignore linter warning opening playlist path * Rename user field Id -> ID * A bunch of renames to address linter warnings * Rename ChatClient -> Client per linter suggestion best practice * Rename ChatServer -> Server per linter suggestion best practice * More linter warning fixes * Add missing comments to all exported functions and properties --- .github/workflows/lint.yml | 2 +- .golangci.yml | 19 +++++++- config/config.go | 6 ++- controllers/admin/chat.go | 9 ++-- controllers/admin/config.go | 20 ++++---- controllers/chat.go | 4 +- controllers/index.go | 6 +-- controllers/logo.go | 2 +- controllers/ping.go | 3 +- core/chat/chat.go | 21 +++++--- core/chat/chatclient.go | 31 ++++++------ core/chat/concurrentConnections.go | 1 + core/chat/events.go | 6 +-- core/chat/events/actionEvent.go | 6 ++- core/chat/events/events.go | 10 ++-- core/chat/events/eventtype.go | 9 ++-- core/chat/events/nameChangeEvent.go | 4 +- core/chat/events/systemMessageEvent.go | 5 +- core/chat/events/userDisabledEvent.go | 2 +- core/chat/events/userJoinedEvent.go | 2 +- core/chat/events/userMessageEvent.go | 3 +- core/chat/messages.go | 3 +- core/chat/persistence.go | 34 ++++++------- core/chat/server.go | 66 ++++++++++++++------------ core/core.go | 2 +- core/data/config.go | 1 + core/data/messages.go | 1 + core/data/migrations.go | 2 +- core/data/persistence.go | 1 + core/data/webhooks.go | 2 +- core/rtmp/rtmp.go | 9 ++-- core/stats.go | 10 ++-- core/status.go | 2 + core/storageproviders/local.go | 1 + core/storageproviders/s3Storage.go | 12 ++--- core/streamState.go | 2 +- core/transcoder/codecs.go | 54 +++++++++++++++++++++ core/transcoder/thumbnailGenerator.go | 11 +++-- core/transcoder/transcoder.go | 5 +- core/transcoder/utils.go | 8 ++-- core/user/externalAPIUser.go | 8 ++-- core/user/user.go | 26 ++++++---- core/webhooks/chat.go | 4 +- core/webhooks/stream.go | 1 + core/webhooks/webhooks.go | 3 ++ logging/logging.go | 2 + logging/paths.go | 1 + metrics/alerting.go | 16 +++---- models/streamOutputVariant.go | 4 +- router/middleware/auth.go | 1 + router/router.go | 8 ++-- utils/accessTokens.go | 1 + utils/backup.go | 13 ++--- utils/nulltime.go | 5 +- utils/phraseGenerator.go | 7 +-- utils/utils.go | 16 ++++--- yp/yp.go | 4 +- 57 files changed, 331 insertions(+), 186 deletions(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 1b0e11450..5e20273bc 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -17,7 +17,7 @@ jobs: uses: golangci/golangci-lint-action@v2 with: # Optional: golangci-lint command line arguments. - args: --issues-exit-code=0 --timeout 2m0s + args: --timeout 5m0s # Optional: working directory, useful for monorepos # working-directory: somedir # Optional: show only new issues if it's a pull request. The default value is `false`. diff --git a/.golangci.yml b/.golangci.yml index 669bda5c3..308825681 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,14 +1,28 @@ run: tests: false + modules-download-mode: readonly issues: + # The linter has a default list of ignorable errors. Turning this on will enable that list. + exclude-use-default: false + + # Maximum issues count per one linter. Set to 0 to disable. Default is 50. max-issues-per-linter: 0 + + # Maximum count of issues with the same text. Set to 0 to disable. Default is 3. max-same-issues: 0 + exclude: + - Subprocess launch(ed with variable|ing should be audited) + - Error return value of .((os\.)?std(out|err)\..*|.*Close|.*Flush|os\.Remove(All)?|.*print(f|ln)?|os\.(Un)?Setenv). is not checked + - G307 # Allow closing files as a defer without checking error. + - composite literal uses unkeyed fields + linters: enable: - bodyclose - dupl + - errcheck - exportloopref - goconst - godot @@ -16,10 +30,13 @@ linters: - goimports - goprintffuncname - gosec + - govet - misspell - prealloc + - revive - rowserrcheck - sqlclosecheck + - staticcheck - unconvert - unparam - whitespace @@ -27,4 +44,4 @@ linters: linters-settings: govet: disable: - - composites + - composite diff --git a/config/config.go b/config/config.go index 3a87c56d0..7445bfe6c 100644 --- a/config/config.go +++ b/config/config.go @@ -22,7 +22,7 @@ var VersionNumber = StaticVersionNumber // WebServerPort is the port for Owncast's webserver that is used for this execution of the service. var WebServerPort = 8080 -// Bind WebServer to this IP address. Be secure by default. +// WebServerIP is the IP address to bind the web server to. All interfaces by default. var WebServerIP = "0.0.0.0" // InternalHLSListenerPort is the port for HLS writes that is used for this execution of the service. @@ -34,6 +34,7 @@ var GitCommit = "" // BuildPlatform is the optional platform this release was built for. var BuildPlatform = "dev" +// GetCommit will return an identifier used for identifying the point in time this build took place. func GetCommit() string { if GitCommit == "" { GitCommit = time.Now().Format("20060102") @@ -42,11 +43,12 @@ func GetCommit() string { return GitCommit } +// DefaultForbiddenUsernames are a list of usernames forbidden from being used in chat. var DefaultForbiddenUsernames = []string{ "owncast", "operator", "admin", "system", } -// The maximum payload we will allow to to be received via the chat socket. +// MaxSocketPayloadSize is the maximum payload we will allow to to be received via the chat socket. const MaxSocketPayloadSize = 2048 // GetReleaseString gets the version string. diff --git a/controllers/admin/chat.go b/controllers/admin/chat.go index cf84f118f..acd7acb2d 100644 --- a/controllers/admin/chat.go +++ b/controllers/admin/chat.go @@ -49,6 +49,7 @@ func UpdateMessageVisibility(w http.ResponseWriter, r *http.Request) { controllers.WriteSimpleResponse(w, true, "changed") } +// UpdateUserEnabled enable or disable a single user by ID. func UpdateUserEnabled(w http.ResponseWriter, r *http.Request) { type blockUserRequest struct { UserID string `json:"userId"` @@ -77,7 +78,7 @@ func UpdateUserEnabled(w http.ResponseWriter, r *http.Request) { // Hide/show the user's chat messages if disabling. // Leave hidden messages hidden to be safe. if !request.Enabled { - if err := chat.SetMessageVisibilityForUserId(request.UserID, request.Enabled); err != nil { + if err := chat.SetMessageVisibilityForUserID(request.UserID, request.Enabled); err != nil { log.Errorln("error changing user messages visibility", err) } } @@ -85,13 +86,14 @@ func UpdateUserEnabled(w http.ResponseWriter, r *http.Request) { // Forcefully disconnect the user from the chat if !request.Enabled { chat.DisconnectUser(request.UserID) - disconnectedUser := user.GetUserById(request.UserID) + disconnectedUser := user.GetUserByID(request.UserID) _ = chat.SendSystemAction(fmt.Sprintf("**%s** has been removed from chat.", disconnectedUser.DisplayName), true) } controllers.WriteSimpleResponse(w, true, fmt.Sprintf("%s enabled: %t", request.UserID, request.Enabled)) } +// GetDisabledUsers will return all the disabled users. func GetDisabledUsers(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") @@ -130,6 +132,7 @@ func SendUserMessage(integration user.ExternalAPIUser, w http.ResponseWriter, r controllers.BadRequestHandler(w, errors.New("no longer supported. see /api/integrations/chat/send")) } +// SendIntegrationChatMessage will send a chat message on behalf of an external chat integration. func SendIntegrationChatMessage(integration user.ExternalAPIUser, w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") @@ -155,7 +158,7 @@ func SendIntegrationChatMessage(integration user.ExternalAPIUser, w http.Respons } event.User = &user.User{ - Id: integration.Id, + ID: integration.ID, DisplayName: name, DisplayColor: integration.DisplayColor, CreatedAt: integration.CreatedAt, diff --git a/controllers/admin/config.go b/controllers/admin/config.go index 39a334555..67903008d 100644 --- a/controllers/admin/config.go +++ b/controllers/admin/config.go @@ -72,6 +72,7 @@ func SetStreamTitle(w http.ResponseWriter, r *http.Request) { controllers.WriteSimpleResponse(w, true, "changed") } +// ExternalSetStreamTitle will change the stream title on behalf of an external integration API request. func ExternalSetStreamTitle(integration user.ExternalAPIUser, w http.ResponseWriter, r *http.Request) { SetStreamTitle(w, r) } @@ -298,11 +299,12 @@ func SetWebServerPort(w http.ResponseWriter, r *http.Request) { if err := data.SetHTTPPortNumber(port); err != nil { controllers.WriteSimpleResponse(w, false, err.Error()) return - } else { - controllers.WriteSimpleResponse(w, true, "HTTP port set") - return } + + controllers.WriteSimpleResponse(w, true, "HTTP port set") + return } + controllers.WriteSimpleResponse(w, false, "Invalid type or value, port must be a number") } @@ -322,14 +324,14 @@ func SetWebServerIP(w http.ResponseWriter, r *http.Request) { if err := data.SetHTTPListenAddress(ip.String()); err != nil { controllers.WriteSimpleResponse(w, false, err.Error()) return - } else { - controllers.WriteSimpleResponse(w, true, "HTTP listen address set") - return } - } else { - controllers.WriteSimpleResponse(w, false, "Invalid IP address") + + controllers.WriteSimpleResponse(w, true, "HTTP listen address set") return } + + controllers.WriteSimpleResponse(w, false, "Invalid IP address") + return } controllers.WriteSimpleResponse(w, false, "Invalid type or value, IP address must be a string") } @@ -427,7 +429,7 @@ func SetS3Configuration(w http.ResponseWriter, r *http.Request) { } if newS3Config.Value.Enabled { - if newS3Config.Value.Endpoint == "" || !utils.IsValidUrl((newS3Config.Value.Endpoint)) { + if newS3Config.Value.Endpoint == "" || !utils.IsValidURL((newS3Config.Value.Endpoint)) { controllers.WriteSimpleResponse(w, false, "s3 support requires an endpoint") return } diff --git a/controllers/chat.go b/controllers/chat.go index 7056f5cb5..2424966d9 100644 --- a/controllers/chat.go +++ b/controllers/chat.go @@ -49,7 +49,7 @@ func RegisterAnonymousChatUser(w http.ResponseWriter, r *http.Request) { } type registerAnonymousUserResponse struct { - Id string `json:"id"` + ID string `json:"id"` AccessToken string `json:"accessToken"` DisplayName string `json:"displayName"` } @@ -67,7 +67,7 @@ func RegisterAnonymousChatUser(w http.ResponseWriter, r *http.Request) { } response := registerAnonymousUserResponse{ - Id: newUser.Id, + ID: newUser.ID, AccessToken: newUser.AccessToken, DisplayName: newUser.DisplayName, } diff --git a/controllers/index.go b/controllers/index.go index d19449566..28a0c7d6e 100644 --- a/controllers/index.go +++ b/controllers/index.go @@ -67,7 +67,7 @@ func IndexHandler(w http.ResponseWriter, r *http.Request) { // Use this as an opportunity to mark this viewer as active. id := utils.GenerateClientIDFromRequest(r) - core.SetViewerIdActive(id) + core.SetViewerIDActive(id) } // Set a cache control max-age header @@ -86,8 +86,8 @@ func handleScraperMetadataPage(w http.ResponseWriter, r *http.Request) { scheme := "http" - if siteUrl := data.GetServerURL(); siteUrl != "" { - if parsed, err := url.Parse(siteUrl); err == nil && parsed.Scheme != "" { + if siteURL := data.GetServerURL(); siteURL != "" { + if parsed, err := url.Parse(siteURL); err == nil && parsed.Scheme != "" { scheme = parsed.Scheme } } diff --git a/controllers/logo.go b/controllers/logo.go index 6db2cc465..1b72a738f 100644 --- a/controllers/logo.go +++ b/controllers/logo.go @@ -94,5 +94,5 @@ func writeBytesAsImage(data []byte, contentType string, w http.ResponseWriter, c } func getImage(path string) ([]byte, error) { - return ioutil.ReadFile(path) + return ioutil.ReadFile(path) // nolint } diff --git a/controllers/ping.go b/controllers/ping.go index 81b0ab6e1..2c125cca2 100644 --- a/controllers/ping.go +++ b/controllers/ping.go @@ -7,7 +7,8 @@ import ( "github.com/owncast/owncast/utils" ) +// Ping is fired by a client to show they are still an active viewer. func Ping(w http.ResponseWriter, r *http.Request) { id := utils.GenerateClientIDFromRequest(r) - core.SetViewerIdActive(id) + core.SetViewerIDActive(id) } diff --git a/core/chat/chat.go b/core/chat/chat.go index 1fde109f0..bf5ab4f35 100644 --- a/core/chat/chat.go +++ b/core/chat/chat.go @@ -12,6 +12,7 @@ import ( var getStatus func() models.Status +// Start begins the chat server. func Start(getStatusFunc func() models.Status) error { setupPersistence() @@ -26,11 +27,11 @@ func Start(getStatusFunc func() models.Status) error { } // GetClientsForUser will return chat connections that are owned by a specific user. -func GetClientsForUser(userID string) ([]*ChatClient, error) { - clients := map[string][]*ChatClient{} +func GetClientsForUser(userID string) ([]*Client, error) { + clients := map[string][]*Client{} for _, client := range _server.clients { - clients[client.User.Id] = append(clients[client.User.Id], client) + clients[client.User.ID] = append(clients[client.User.ID], client) } if _, exists := clients[userID]; !exists { @@ -40,8 +41,9 @@ func GetClientsForUser(userID string) ([]*ChatClient, error) { return clients[userID], nil } -func GetClients() []*ChatClient { - clients := []*ChatClient{} +// GetClients will return all the current chat clients connected. +func GetClients() []*Client { + clients := []*Client{} // Convert the keyed map to a slice. for _, client := range _server.clients { @@ -55,6 +57,7 @@ func GetClients() []*ChatClient { return clients } +// SendSystemMessage will send a message string as a system message to all clients. func SendSystemMessage(text string, ephemeral bool) error { message := events.SystemMessageEvent{ MessageEvent: events.MessageEvent{ @@ -69,12 +72,13 @@ func SendSystemMessage(text string, ephemeral bool) error { } if !ephemeral { - saveEvent(message.Id, "system", message.Body, message.GetMessageType(), nil, message.Timestamp) + saveEvent(message.ID, "system", message.Body, message.GetMessageType(), nil, message.Timestamp) } return nil } +// SendSystemAction will send a system action string as an action event to all clients. func SendSystemAction(text string, ephemeral bool) error { message := events.ActionEvent{ MessageEvent: events.MessageEvent{ @@ -90,20 +94,23 @@ func SendSystemAction(text string, ephemeral bool) error { } if !ephemeral { - saveEvent(message.Id, "action", message.Body, message.GetMessageType(), nil, message.Timestamp) + saveEvent(message.ID, "action", message.Body, message.GetMessageType(), nil, message.Timestamp) } return nil } +// SendAllWelcomeMessage will send the chat message to all connected clients. func SendAllWelcomeMessage() { _server.sendAllWelcomeMessage() } +// Broadcast will send all connected clients the outbound object provided. func Broadcast(event events.OutboundEvent) error { return _server.Broadcast(event.GetBroadcastPayload()) } +// HandleClientConnection handles a single inbound websocket connection. func HandleClientConnection(w http.ResponseWriter, r *http.Request) { _server.HandleClientConnection(w, r) } diff --git a/core/chat/chatclient.go b/core/chat/chatclient.go index 3d90dbe2b..c0635989d 100644 --- a/core/chat/chatclient.go +++ b/core/chat/chatclient.go @@ -15,12 +15,13 @@ import ( "github.com/owncast/owncast/geoip" ) -type ChatClient struct { +// Client represents a single chat client. +type Client struct { id uint accessToken string conn *websocket.Conn User *user.User `json:"user"` - server *ChatServer + server *Server ipAddress string `json:"-"` // Buffered channel of outbound messages. send chan []byte @@ -35,7 +36,7 @@ type ChatClient struct { type chatClientEvent struct { data []byte - client *ChatClient + client *Client } const ( @@ -64,7 +65,7 @@ var ( space = []byte{' '} ) -func (c *ChatClient) sendConnectedClientInfo() { +func (c *Client) sendConnectedClientInfo() { payload := events.EventPayload{ "type": events.ConnectedUserInfo, "user": c.User, @@ -73,7 +74,7 @@ func (c *ChatClient) sendConnectedClientInfo() { c.sendPayload(payload) } -func (c *ChatClient) readPump() { +func (c *Client) readPump() { // Allow 3 messages every two seconds. limit := rate.Every(2 * time.Second / 3) c.rateLimiter = rate.NewLimiter(limit, 1) @@ -122,11 +123,11 @@ func (c *ChatClient) readPump() { } } -func (c *ChatClient) writePump() { +func (c *Client) writePump() { ticker := time.NewTicker(pingPeriod) defer func() { ticker.Stop() - c.conn.Close() + _ = c.conn.Close() }() for { @@ -167,14 +168,14 @@ func (c *ChatClient) writePump() { } } -func (c *ChatClient) handleEvent(data []byte) { +func (c *Client) handleEvent(data []byte) { c.server.inbound <- chatClientEvent{data: data, client: c} } -func (c *ChatClient) close() { +func (c *Client) close() { log.Traceln("client closed:", c.User.DisplayName, c.id, c.ipAddress) - c.conn.Close() + _ = c.conn.Close() c.server.unregister <- c.id if c.send != nil { close(c.send) @@ -182,18 +183,18 @@ func (c *ChatClient) close() { } } -func (c *ChatClient) passesRateLimit() bool { +func (c *Client) passesRateLimit() bool { return c.rateLimiter.Allow() && !c.inTimeout } -func (c *ChatClient) startChatRejectionTimeout() { +func (c *Client) startChatRejectionTimeout() { if c.timeoutTimer != nil { return } c.inTimeout = true c.timeoutTimer = time.NewTimer(10 * time.Second) - go func(c *ChatClient) { + go func(c *Client) { for range c.timeoutTimer.C { c.inTimeout = false c.timeoutTimer = nil @@ -203,7 +204,7 @@ func (c *ChatClient) startChatRejectionTimeout() { c.sendAction("You are temporarily blocked from sending chat messages due to perceived flooding.") } -func (c *ChatClient) sendPayload(payload events.EventPayload) { +func (c *Client) sendPayload(payload events.EventPayload) { var data []byte data, err := json.Marshal(payload) if err != nil { @@ -216,7 +217,7 @@ func (c *ChatClient) sendPayload(payload events.EventPayload) { } } -func (c *ChatClient) sendAction(message string) { +func (c *Client) sendAction(message string) { clientMessage := events.ActionEvent{ MessageEvent: events.MessageEvent{ Body: message, diff --git a/core/chat/concurrentConnections.go b/core/chat/concurrentConnections.go index e96e78015..ab3912a30 100644 --- a/core/chat/concurrentConnections.go +++ b/core/chat/concurrentConnections.go @@ -1,3 +1,4 @@ +// nolint:goimports // +build !freebsd package chat diff --git a/core/chat/events.go b/core/chat/events.go index 6b25bc3fb..7c2c664eb 100644 --- a/core/chat/events.go +++ b/core/chat/events.go @@ -13,7 +13,7 @@ import ( log "github.com/sirupsen/logrus" ) -func (s *ChatServer) userNameChanged(eventData chatClientEvent) { +func (s *Server) userNameChanged(eventData chatClientEvent) { var receivedEvent events.NameChangeEvent if err := json.Unmarshal(eventData.data, &receivedEvent); err != nil { log.Errorln("error unmarshalling to NameChangeEvent", err) @@ -43,7 +43,7 @@ func (s *ChatServer) userNameChanged(eventData chatClientEvent) { oldName := savedUser.DisplayName // Save the new name - user.ChangeUsername(eventData.client.User.Id, receivedEvent.NewName) + user.ChangeUsername(eventData.client.User.ID, receivedEvent.NewName) // Update the connected clients associated user with the new name now := time.Now() @@ -69,7 +69,7 @@ func (s *ChatServer) userNameChanged(eventData chatClientEvent) { webhooks.SendChatEventUsernameChanged(receivedEvent) } -func (s *ChatServer) userMessageSent(eventData chatClientEvent) { +func (s *Server) userMessageSent(eventData chatClientEvent) { var event events.UserMessageEvent if err := json.Unmarshal(eventData.data, &event); err != nil { log.Errorln("error unmarshalling to UserMessageEvent", err) diff --git a/core/chat/events/actionEvent.go b/core/chat/events/actionEvent.go index 8f8093a03..b251b8511 100644 --- a/core/chat/events/actionEvent.go +++ b/core/chat/events/actionEvent.go @@ -1,20 +1,22 @@ package events +// ActionEvent represents an action that took place, not a chat message. type ActionEvent struct { Event MessageEvent } -// ActionEvent will return the object to send to all chat users. +// GetBroadcastPayload will return the object to send to all chat users. func (e *ActionEvent) GetBroadcastPayload() EventPayload { return EventPayload{ - "id": e.Id, + "id": e.ID, "timestamp": e.Timestamp, "body": e.Body, "type": e.GetMessageType(), } } +// GetMessageType will return the type of message. func (e *ActionEvent) GetMessageType() EventType { return ChatActionSent } diff --git a/core/chat/events/events.go b/core/chat/events/events.go index 18fdfee6b..7f008e915 100644 --- a/core/chat/events/events.go +++ b/core/chat/events/events.go @@ -20,6 +20,7 @@ import ( // EventPayload is a generic key/value map for sending out to chat clients. type EventPayload map[string]interface{} +// OutboundEvent represents an event that is sent out to all listeners of the chat server. type OutboundEvent interface { GetBroadcastPayload() EventPayload GetMessageType() EventType @@ -28,10 +29,11 @@ type OutboundEvent interface { // Event is any kind of event. A type is required to be specified. type Event struct { Type EventType `json:"type,omitempty"` - Id string `json:"id"` + ID string `json:"id"` Timestamp time.Time `json:"timestamp"` } +// UserEvent is an event with an associated user. type UserEvent struct { User *user.User `json:"user"` HiddenAt *time.Time `json:"hiddenAt,omitempty"` @@ -44,6 +46,7 @@ type MessageEvent struct { RawBody string `json:"-"` } +// SystemActionEvent is an event that represents an action that took place, not a chat message. type SystemActionEvent struct { Event MessageEvent @@ -51,13 +54,13 @@ type SystemActionEvent struct { // SetDefaults will set default properties of all inbound events. func (e *Event) SetDefaults() { - e.Id = shortid.MustGenerate() + e.ID = shortid.MustGenerate() e.Timestamp = time.Now() } // SetDefaults will set default properties of all inbound events. func (e *UserMessageEvent) SetDefaults() { - e.Id = shortid.MustGenerate() + e.ID = shortid.MustGenerate() e.Timestamp = time.Now() e.RenderAndSanitizeMessageBody() } @@ -92,6 +95,7 @@ func RenderAndSanitize(raw string) string { return strings.TrimSpace(safe) } +// RenderMarkdown will return HTML rendered from the string body of a chat message. func RenderMarkdown(raw string) string { markdown := goldmark.New( goldmark.WithRendererOptions( diff --git a/core/chat/events/eventtype.go b/core/chat/events/eventtype.go index aaed78257..1b06b6513 100644 --- a/core/chat/events/eventtype.go +++ b/core/chat/events/eventtype.go @@ -27,8 +27,11 @@ const ( // ConnectedUserInfo is a private event to a user letting them know their user details. ConnectedUserInfo EventType = "CONNECTED_USER_INFO" // ChatActionSent is a generic chat action that can be used for anything that doesn't need specific handling or formatting. - ChatActionSent EventType = "CHAT_ACTION" - ErrorNeedsRegistration EventType = "ERROR_NEEDS_REGISTRATION" + ChatActionSent EventType = "CHAT_ACTION" + // ErrorNeedsRegistration is an error returned when the client needs to perform registration. + ErrorNeedsRegistration EventType = "ERROR_NEEDS_REGISTRATION" + // ErrorMaxConnectionsExceeded is an error returned when the server determined it should not handle more connections. ErrorMaxConnectionsExceeded EventType = "ERROR_MAX_CONNECTIONS_EXCEEDED" - ErrorUserDisabled EventType = "ERROR_USER_DISABLED" + // ErrorUserDisabled is an error returned when the connecting user has been previously banned/disabled. + ErrorUserDisabled EventType = "ERROR_USER_DISABLED" ) diff --git a/core/chat/events/nameChangeEvent.go b/core/chat/events/nameChangeEvent.go index 7eb898644..e782d90b9 100644 --- a/core/chat/events/nameChangeEvent.go +++ b/core/chat/events/nameChangeEvent.go @@ -7,7 +7,7 @@ type NameChangeEvent struct { NewName string `json:"newName"` } -// NameChangeEventBroadcast is fired when a user changes their chat display name. +// NameChangeBroadcast represents a user changing their chat display name. type NameChangeBroadcast struct { Event UserEvent @@ -17,7 +17,7 @@ type NameChangeBroadcast struct { // GetBroadcastPayload will return the object to send to all chat users. func (e *NameChangeBroadcast) GetBroadcastPayload() EventPayload { return EventPayload{ - "id": e.Id, + "id": e.ID, "timestamp": e.Timestamp, "user": e.User, "oldName": e.Oldname, diff --git a/core/chat/events/systemMessageEvent.go b/core/chat/events/systemMessageEvent.go index 0bb79081c..7f5f1e732 100644 --- a/core/chat/events/systemMessageEvent.go +++ b/core/chat/events/systemMessageEvent.go @@ -8,10 +8,10 @@ type SystemMessageEvent struct { MessageEvent } -// SystemMessageEvent will return the object to send to all chat users. +// GetBroadcastPayload will return the object to send to all chat users. func (e *SystemMessageEvent) GetBroadcastPayload() EventPayload { return EventPayload{ - "id": e.Id, + "id": e.ID, "timestamp": e.Timestamp, "body": e.Body, "type": SystemMessageSent, @@ -21,6 +21,7 @@ func (e *SystemMessageEvent) GetBroadcastPayload() EventPayload { } } +// GetMessageType will return the event type for this message. func (e *SystemMessageEvent) GetMessageType() EventType { return SystemMessageSent } diff --git a/core/chat/events/userDisabledEvent.go b/core/chat/events/userDisabledEvent.go index 4f5b4b3c9..d869210ef 100644 --- a/core/chat/events/userDisabledEvent.go +++ b/core/chat/events/userDisabledEvent.go @@ -10,7 +10,7 @@ type UserDisabledEvent struct { func (e *UserDisabledEvent) GetBroadcastPayload() EventPayload { return EventPayload{ "type": ErrorUserDisabled, - "id": e.Id, + "id": e.ID, "timestamp": e.Timestamp, "user": e.User, } diff --git a/core/chat/events/userJoinedEvent.go b/core/chat/events/userJoinedEvent.go index dfa332fdf..475b74ce5 100644 --- a/core/chat/events/userJoinedEvent.go +++ b/core/chat/events/userJoinedEvent.go @@ -10,7 +10,7 @@ type UserJoinedEvent struct { func (e *UserJoinedEvent) GetBroadcastPayload() EventPayload { return EventPayload{ "type": UserJoined, - "id": e.Id, + "id": e.ID, "timestamp": e.Timestamp, "user": e.User, } diff --git a/core/chat/events/userMessageEvent.go b/core/chat/events/userMessageEvent.go index 63f9fb567..f0dd95e91 100644 --- a/core/chat/events/userMessageEvent.go +++ b/core/chat/events/userMessageEvent.go @@ -10,7 +10,7 @@ type UserMessageEvent struct { // GetBroadcastPayload will return the object to send to all chat users. func (e *UserMessageEvent) GetBroadcastPayload() EventPayload { return EventPayload{ - "id": e.Id, + "id": e.ID, "timestamp": e.Timestamp, "body": e.Body, "user": e.User, @@ -19,6 +19,7 @@ func (e *UserMessageEvent) GetBroadcastPayload() EventPayload { } } +// GetMessageType will return the event type for this message. func (e *UserMessageEvent) GetMessageType() EventType { return MessageSent } diff --git a/core/chat/messages.go b/core/chat/messages.go index 9d35be716..e55a28767 100644 --- a/core/chat/messages.go +++ b/core/chat/messages.go @@ -6,6 +6,7 @@ import ( log "github.com/sirupsen/logrus" ) +// SetMessagesVisibility will set the visibility of multiple messages by ID. func SetMessagesVisibility(messageIDs []string, visibility bool) error { // Save new message visibility if err := saveMessageVisibility(messageIDs, visibility); err != nil { @@ -17,7 +18,7 @@ func SetMessagesVisibility(messageIDs []string, visibility bool) error { // Note: Our client expects a single message at a time, so we can't just // send an array of messages in a single update. for _, id := range messageIDs { - message, err := getMessageById(id) + message, err := getMessageByID(id) if err != nil { log.Errorln(err) continue diff --git a/core/chat/persistence.go b/core/chat/persistence.go index 161436ad9..082ffc4f8 100644 --- a/core/chat/persistence.go +++ b/core/chat/persistence.go @@ -5,7 +5,6 @@ import ( "strings" "time" - _ "github.com/mattn/go-sqlite3" "github.com/owncast/owncast/core/chat/events" "github.com/owncast/owncast/core/data" "github.com/owncast/owncast/core/user" @@ -33,11 +32,12 @@ func setupPersistence() { }() } +// SaveUserMessage will save a single chat event to the messages database. func SaveUserMessage(event events.UserMessageEvent) { - saveEvent(event.Id, event.User.Id, event.Body, event.Type, event.HiddenAt, event.Timestamp) + saveEvent(event.ID, event.User.ID, event.Body, event.Type, event.HiddenAt, event.Timestamp) } -func saveEvent(id string, userId string, body string, eventType string, hidden *time.Time, timestamp time.Time) { +func saveEvent(id string, userID string, body string, eventType string, hidden *time.Time, timestamp time.Time) { defer func() { _historyCache = nil }() @@ -61,7 +61,7 @@ func saveEvent(id string, userId string, body string, eventType string, hidden * defer stmt.Close() - if _, err = stmt.Exec(id, userId, body, eventType, hidden, timestamp); err != nil { + if _, err = stmt.Exec(id, userID, body, eventType, hidden, timestamp); err != nil { log.Errorln("error saving", eventType, err) return } @@ -82,7 +82,7 @@ func getChat(query string) []events.UserMessageEvent { for rows.Next() { var id string - var userId string + var userID string var body string var messageType models.EventType var hiddenAt *time.Time @@ -96,7 +96,7 @@ func getChat(query string) []events.UserMessageEvent { var userNameChangedAt *time.Time // Convert a database row into a chat event - err = rows.Scan(&id, &userId, &body, &messageType, &hiddenAt, ×tamp, &userDisplayName, &userDisplayColor, &userCreatedAt, &userDisabledAt, &previousUsernames, &userNameChangedAt) + err = rows.Scan(&id, &userID, &body, &messageType, &hiddenAt, ×tamp, &userDisplayName, &userDisplayColor, &userCreatedAt, &userDisabledAt, &previousUsernames, &userNameChangedAt) if err != nil { log.Errorln("There is a problem converting query to chat objects. Please report this:", query) break @@ -120,7 +120,7 @@ func getChat(query string) []events.UserMessageEvent { } user := user.User{ - Id: userId, + ID: userID, AccessToken: "", DisplayName: *userDisplayName, DisplayColor: *userDisplayColor, @@ -133,7 +133,7 @@ func getChat(query string) []events.UserMessageEvent { message := events.UserMessageEvent{ Event: events.Event{ Type: messageType, - Id: id, + ID: id, Timestamp: timestamp, }, UserEvent: events.UserEvent{ @@ -154,6 +154,7 @@ func getChat(query string) []events.UserMessageEvent { var _historyCache *[]events.UserMessageEvent +// GetChatModerationHistory will return all the chat messages suitable for moderation purposes. func GetChatModerationHistory() []events.UserMessageEvent { if _historyCache != nil { return *_historyCache @@ -168,6 +169,7 @@ func GetChatModerationHistory() []events.UserMessageEvent { return result } +// GetChatHistory will return all the chat messages suitable for returning as user-facing chat history. func GetChatHistory() []events.UserMessageEvent { // Get all visible messages var query = fmt.Sprintf("SELECT messages.id, user_id, body, eventType, hidden_at, timestamp, display_name, display_color, created_at, disabled_at, previous_names, namechanged_at FROM messages, users WHERE messages.user_id = users.id AND hidden_at IS NULL AND disabled_at IS NULL ORDER BY timestamp DESC LIMIT %d", maxBacklogNumber) @@ -181,9 +183,9 @@ func GetChatHistory() []events.UserMessageEvent { return m } -// SetMessageVisibilityForUserId will bulk change the visibility of messages for a user +// SetMessageVisibilityForUserID will bulk change the visibility of messages for a user // and then send out visibility changed events to chat clients. -func SetMessageVisibilityForUserId(userID string, visible bool) error { +func SetMessageVisibilityForUserID(userID string, visible bool) error { defer func() { _historyCache = nil }() @@ -198,7 +200,7 @@ func SetMessageVisibilityForUserId(userID string, visible bool) error { } for _, message := range messages { - ids = append(ids, message.Id) + ids = append(ids, message.ID) } // Tell the clients to hide/show these messages. @@ -250,29 +252,29 @@ func saveMessageVisibility(messageIDs []string, visible bool) error { return nil } -func getMessageById(messageID string) (*events.UserMessageEvent, error) { +func getMessageByID(messageID string) (*events.UserMessageEvent, error) { var query = "SELECT * FROM messages WHERE id = ?" row := _datastore.DB.QueryRow(query, messageID) var id string - var userId string + var userID string var body string var eventType models.EventType var hiddenAt *time.Time var timestamp time.Time - err := row.Scan(&id, &userId, &body, &eventType, &hiddenAt, ×tamp) + err := row.Scan(&id, &userID, &body, &eventType, &hiddenAt, ×tamp) if err != nil { log.Errorln(err) return nil, err } - user := user.GetUserById(userId) + user := user.GetUserByID(userID) return &events.UserMessageEvent{ events.Event{ Type: eventType, - Id: id, + ID: id, Timestamp: timestamp, }, events.UserEvent{ diff --git a/core/chat/server.go b/core/chat/server.go index c85b9a8f3..72a41a191 100644 --- a/core/chat/server.go +++ b/core/chat/server.go @@ -18,12 +18,13 @@ import ( "github.com/owncast/owncast/utils" ) -var _server *ChatServer +var _server *Server -type ChatServer struct { +// Server represents an instance of the chat server. +type Server struct { mu sync.RWMutex seq uint - clients map[uint]*ChatClient + clients map[uint]*Client maxSocketConnectionLimit int64 // send outbound message payload to all clients @@ -36,12 +37,13 @@ type ChatServer struct { unregister chan uint // the ChatClient id } -func NewChat() *ChatServer { +// NewChat will return a new instance of the chat server. +func NewChat() *Server { maximumConcurrentConnectionLimit := getMaximumConcurrentConnectionLimit() setSystemConcurrentConnectionLimit(maximumConcurrentConnectionLimit) - server := &ChatServer{ - clients: map[uint]*ChatClient{}, + server := &Server{ + clients: map[uint]*Client{}, outbound: make(chan []byte), inbound: make(chan chatClientEvent), unregister: make(chan uint), @@ -51,13 +53,14 @@ func NewChat() *ChatServer { return server } -func (s *ChatServer) Run() { +// Run will start the chat server. +func (s *Server) Run() { for { select { - case clientId := <-s.unregister: - if _, ok := s.clients[clientId]; ok { + case clientID := <-s.unregister: + if _, ok := s.clients[clientID]; ok { s.mu.Lock() - delete(s.clients, clientId) + delete(s.clients, clientID) s.mu.Unlock() } @@ -68,8 +71,8 @@ func (s *ChatServer) Run() { } // Addclient registers new connection as a User. -func (s *ChatServer) Addclient(conn *websocket.Conn, user *user.User, accessToken string, userAgent string, ipAddress string) *ChatClient { - client := &ChatClient{ +func (s *Server) Addclient(conn *websocket.Conn, user *user.User, accessToken string, userAgent string, ipAddress string) *Client { + client := &Client{ server: s, conn: conn, User: user, @@ -101,14 +104,14 @@ func (s *ChatServer) Addclient(conn *websocket.Conn, user *user.User, accessToke } // Asynchronously, optionally, fetch GeoIP data. - go func(client *ChatClient) { + go func(client *Client) { client.Geo = geoip.GetGeoFromIP(ipAddress) }(client) return client } -func (s *ChatServer) sendUserJoinedMessage(c *ChatClient) { +func (s *Server) sendUserJoinedMessage(c *Client) { userJoinedEvent := events.UserJoinedEvent{} userJoinedEvent.SetDefaults() userJoinedEvent.User = c.User @@ -121,7 +124,8 @@ func (s *ChatServer) sendUserJoinedMessage(c *ChatClient) { webhooks.SendChatEventUserJoined(userJoinedEvent) } -func (s *ChatServer) ClientClosed(c *ChatClient) { +// ClientClosed is fired when a client disconnects or connection is dropped. +func (s *Server) ClientClosed(c *Client) { s.mu.Lock() defer s.mu.Unlock() c.close() @@ -132,7 +136,8 @@ func (s *ChatServer) ClientClosed(c *ChatClient) { } } -func (s *ChatServer) HandleClientConnection(w http.ResponseWriter, r *http.Request) { +// HandleClientConnection is fired when a single client connects to the websocket. +func (s *Server) HandleClientConnection(w http.ResponseWriter, r *http.Request) { if data.GetChatDisabled() { _, _ = w.Write([]byte(events.ChatDisabled)) return @@ -155,7 +160,7 @@ func (s *ChatServer) HandleClientConnection(w http.ResponseWriter, r *http.Reque if accessToken == "" { log.Errorln("Access token is required") // Return HTTP status code - conn.Close() + _ = conn.Close() return } @@ -166,17 +171,17 @@ func (s *ChatServer) HandleClientConnection(w http.ResponseWriter, r *http.Reque "type": events.ErrorNeedsRegistration, }) // Send error that registration is required - conn.Close() + _ = conn.Close() return } // User is disabled therefore we should disconnect. if user.DisabledAt != nil { - log.Traceln("Disabled user", user.Id, user.DisplayName, "rejected") + log.Traceln("Disabled user", user.ID, user.DisplayName, "rejected") _ = conn.WriteJSON(events.EventPayload{ "type": events.ErrorUserDisabled, }) - conn.Close() + _ = conn.Close() return } @@ -187,7 +192,7 @@ func (s *ChatServer) HandleClientConnection(w http.ResponseWriter, r *http.Reque } // Broadcast sends message to all connected clients. -func (s *ChatServer) Broadcast(payload events.EventPayload) error { +func (s *Server) Broadcast(payload events.EventPayload) error { data, err := json.Marshal(payload) if err != nil { return err @@ -212,7 +217,8 @@ func (s *ChatServer) Broadcast(payload events.EventPayload) error { return nil } -func (s *ChatServer) Send(payload events.EventPayload, client *ChatClient) { +// Send will send a single payload to a single connected client. +func (s *Server) Send(payload events.EventPayload, client *Client) { data, err := json.Marshal(payload) if err != nil { log.Errorln(err) @@ -223,7 +229,7 @@ func (s *ChatServer) Send(payload events.EventPayload, client *ChatClient) { } // DisconnectUser will forcefully disconnect all clients belonging to a user by ID. -func (s *ChatServer) DisconnectUser(userID string) { +func (s *Server) DisconnectUser(userID string) { s.mu.Lock() clients, err := GetClientsForUser(userID) s.mu.Unlock() @@ -234,9 +240,9 @@ func (s *ChatServer) DisconnectUser(userID string) { } for _, client := range clients { - log.Traceln("Disconnecting client", client.User.Id, "owned by", client.User.DisplayName) + log.Traceln("Disconnecting client", client.User.ID, "owned by", client.User.DisplayName) - go func(client *ChatClient) { + go func(client *Client) { event := events.UserDisabledEvent{} event.SetDefaults() @@ -257,7 +263,7 @@ func (s *ChatServer) DisconnectUser(userID string) { } } -func (s *ChatServer) eventReceived(event chatClientEvent) { +func (s *Server) eventReceived(event chatClientEvent) { var typecheck map[string]interface{} if err := json.Unmarshal(event.data, &typecheck); err != nil { log.Debugln(err) @@ -277,7 +283,7 @@ func (s *ChatServer) eventReceived(event chatClientEvent) { } } -func (s *ChatServer) sendWelcomeMessageToClient(c *ChatClient) { +func (s *Server) sendWelcomeMessageToClient(c *Client) { // Add an artificial delay so people notice this message come in. time.Sleep(7 * time.Second) @@ -288,7 +294,7 @@ func (s *ChatServer) sendWelcomeMessageToClient(c *ChatClient) { } } -func (s *ChatServer) sendAllWelcomeMessage() { +func (s *Server) sendAllWelcomeMessage() { welcomeMessage := utils.RenderSimpleMarkdown(data.GetServerWelcomeMessage()) if welcomeMessage != "" { @@ -303,7 +309,7 @@ func (s *ChatServer) sendAllWelcomeMessage() { } } -func (s *ChatServer) sendSystemMessageToClient(c *ChatClient, message string) { +func (s *Server) sendSystemMessageToClient(c *Client, message string) { clientMessage := events.SystemMessageEvent{ Event: events.Event{}, MessageEvent: events.MessageEvent{ @@ -314,7 +320,7 @@ func (s *ChatServer) sendSystemMessageToClient(c *ChatClient, message string) { s.Send(clientMessage.GetBroadcastPayload(), c) } -func (s *ChatServer) sendActionToClient(c *ChatClient, message string) { +func (s *Server) sendActionToClient(c *Client, message string) { clientMessage := events.ActionEvent{ MessageEvent: events.MessageEvent{ Body: message, diff --git a/core/core.go b/core/core.go index a9818d3d7..6a1fb1884 100644 --- a/core/core.go +++ b/core/core.go @@ -112,7 +112,7 @@ func transitionToOfflineVideoStreamContent() { } // Delete the preview Gif - os.Remove(path.Join(config.WebRoot, "preview.gif")) + _ = os.Remove(path.Join(config.WebRoot, "preview.gif")) } func resetDirectories() { diff --git a/core/data/config.go b/core/data/config.go index 64b1304ac..c8beb0152 100644 --- a/core/data/config.go +++ b/core/data/config.go @@ -516,6 +516,7 @@ func SetVideoCodec(codec string) error { return _datastore.SetString(videoCodecKey, codec) } +// GetVideoCodec returns the codec to use for transcoding video. func GetVideoCodec() string { codec, err := _datastore.GetString(videoCodecKey) if codec == "" || err != nil { diff --git a/core/data/messages.go b/core/data/messages.go index 5735e336a..efb9d49e9 100644 --- a/core/data/messages.go +++ b/core/data/messages.go @@ -6,6 +6,7 @@ import ( log "github.com/sirupsen/logrus" ) +// CreateMessagesTable will create the chat messages table if needed. func CreateMessagesTable(db *sql.DB) { createTableSQL := `CREATE TABLE IF NOT EXISTS messages ( "id" string NOT NULL, diff --git a/core/data/migrations.go b/core/data/migrations.go index 0e614719e..270d5f3d5 100644 --- a/core/data/migrations.go +++ b/core/data/migrations.go @@ -64,7 +64,7 @@ func migrateToSchema1(db *sql.DB) { return } - var lastUsed *time.Time = nil + var lastUsed *time.Time if lastUsedString != nil { lastUsedTime, _ := time.Parse(time.RFC3339, *lastUsedString) lastUsed = &lastUsedTime diff --git a/core/data/persistence.go b/core/data/persistence.go index deb677769..d1a6c9432 100644 --- a/core/data/persistence.go +++ b/core/data/persistence.go @@ -144,6 +144,7 @@ func (ds *Datastore) Reset() { PopulateDefaults() } +// GetDatastore returns the shared instance of the owncast datastore. func GetDatastore() *Datastore { return _datastore } diff --git a/core/data/webhooks.go b/core/data/webhooks.go index 073c30631..c7f5d78bf 100644 --- a/core/data/webhooks.go +++ b/core/data/webhooks.go @@ -169,7 +169,7 @@ func GetWebhooks() ([]models.Webhook, error) { //nolint return webhooks, err } - var lastUsed *time.Time = nil + var lastUsed *time.Time if lastUsedString != nil { lastUsedTime, _ := time.Parse(time.RFC3339, *lastUsedString) lastUsed = &lastUsedTime diff --git a/core/rtmp/rtmp.go b/core/rtmp/rtmp.go index e5311c001..0acd92299 100644 --- a/core/rtmp/rtmp.go +++ b/core/rtmp/rtmp.go @@ -60,6 +60,7 @@ func Start(setStreamAsConnected func(*io.PipeReader), setBroadcaster func(models } } +// HandleConn is fired when an inbound RTMP connection takes place. func HandleConn(c *rtmp.Conn, nc net.Conn) { c.LogTagEvent = func(isRead bool, t flvio.Tag) { if t.Type == flvio.TAG_AMF0 { @@ -70,13 +71,13 @@ func HandleConn(c *rtmp.Conn, nc net.Conn) { if _hasInboundRTMPConnection { log.Errorln("stream already running; can not overtake an existing stream") - nc.Close() + _ = nc.Close() return } if !secretMatch(data.GetStreamKey(), c.URL.Path) { log.Errorln("invalid streaming key; rejecting incoming stream") - nc.Close() + _ = nc.Close() return } @@ -129,8 +130,8 @@ func handleDisconnect(conn net.Conn) { } log.Infoln("Inbound stream disconnected.") - conn.Close() - _pipe.Close() + _ = conn.Close() + _ = _pipe.Close() _hasInboundRTMPConnection = false } diff --git a/core/stats.go b/core/stats.go index f74ab8eab..1398dbf0e 100644 --- a/core/stats.go +++ b/core/stats.go @@ -61,8 +61,8 @@ func RemoveChatClient(clientID string) { l.Unlock() } -// SetViewerIdActive sets a client as active and connected. -func SetViewerIdActive(id string) { +// SetViewerIDActive sets a client as active and connected. +func SetViewerIDActive(id string) { l.Lock() defer l.Unlock() @@ -81,10 +81,10 @@ func pruneViewerCount() { l.Lock() defer l.Unlock() - for viewerId := range _stats.Viewers { - viewerLastSeenTime := _stats.Viewers[viewerId] + for viewerID := range _stats.Viewers { + viewerLastSeenTime := _stats.Viewers[viewerID] if time.Since(viewerLastSeenTime) < _activeViewerPurgeTimeout { - viewers[viewerId] = viewerLastSeenTime + viewers[viewerID] = viewerLastSeenTime } } diff --git a/core/status.go b/core/status.go index 5981a62ba..e0408b828 100644 --- a/core/status.go +++ b/core/status.go @@ -29,6 +29,7 @@ func GetStatus() models.Status { } } +// GetCurrentBroadcast will return the currently active broadcast. func GetCurrentBroadcast() *models.CurrentBroadcast { return _currentBroadcast } @@ -38,6 +39,7 @@ func setBroadcaster(broadcaster models.Broadcaster) { _broadcaster = &broadcaster } +// GetBroadcaster will return the details of the currently active broadcaster. func GetBroadcaster() *models.Broadcaster { return _broadcaster } diff --git a/core/storageproviders/local.go b/core/storageproviders/local.go index a447ee697..ee75b178b 100644 --- a/core/storageproviders/local.go +++ b/core/storageproviders/local.go @@ -11,6 +11,7 @@ import ( "github.com/owncast/owncast/utils" ) +// LocalStorage represents an instance of the local storage provider for HLS video. type LocalStorage struct { } diff --git a/core/storageproviders/s3Storage.go b/core/storageproviders/s3Storage.go index 99f8a6ddf..bcee60a28 100644 --- a/core/storageproviders/s3Storage.go +++ b/core/storageproviders/s3Storage.go @@ -25,7 +25,7 @@ import ( // then keep a reference to it here. var _queuedPlaylistUpdates = make(map[string]string) -// S3Storage is the s3 implementation of the ChunkStorageProvider. +// S3Storage is the s3 implementation of a storage provider. type S3Storage struct { sess *session.Session host string @@ -124,7 +124,7 @@ func (s *S3Storage) MasterPlaylistWritten(localFilePath string) { // Save saves the file to the s3 bucket. func (s *S3Storage) Save(filePath string, retryCount int) (string, error) { - file, err := os.Open(filePath) + file, err := os.Open(filePath) // nolint if err != nil { return "", err } @@ -153,10 +153,10 @@ func (s *S3Storage) Save(filePath string, retryCount int) (string, error) { if retryCount < 4 { log.Traceln("Retrying...") return s.Save(filePath, retryCount+1) - } else { - log.Warnln("Giving up on", filePath, err) - return "", fmt.Errorf("Giving up on %s", filePath) } + + log.Warnln("Giving up on", filePath, err) + return "", fmt.Errorf("Giving up on %s", filePath) } return response.Location, nil @@ -185,7 +185,7 @@ func (s *S3Storage) connectAWS() *session.Session { // rewriteRemotePlaylist will take a local playlist and rewrite it to have absolute URLs to remote locations. func (s *S3Storage) rewriteRemotePlaylist(filePath string) error { - f, err := os.Open(filePath) + f, err := os.Open(filePath) // nolint if err != nil { panic(err) } diff --git a/core/streamState.go b/core/streamState.go index c60bbc5b5..0c54a948c 100644 --- a/core/streamState.go +++ b/core/streamState.go @@ -110,7 +110,7 @@ func SetStreamAsDisconnected() { log.Warnln(err) } if utils.DoesFileExists(playlistFilePath) { - f, err := os.OpenFile(playlistFilePath, os.O_CREATE|os.O_RDWR, os.ModePerm) + f, err := os.OpenFile(playlistFilePath, os.O_CREATE|os.O_RDWR, os.ModePerm) //nolint if err != nil { log.Errorln(err) } diff --git a/core/transcoder/codecs.go b/core/transcoder/codecs.go index 36e469e2b..01479600a 100644 --- a/core/transcoder/codecs.go +++ b/core/transcoder/codecs.go @@ -29,35 +29,43 @@ var supportedCodecs = map[string]string{ (&NvencCodec{}).Name(): "NVIDIA nvenc", } +// Libx264Codec represents an instance of the Libx264 Codec. type Libx264Codec struct { } +// Name returns the codec name. func (c *Libx264Codec) Name() string { return "libx264" } +// DisplayName returns the human readable name of the codec. func (c *Libx264Codec) DisplayName() string { return "x264" } +// GlobalFlags are the global flags used with this codec in the transcoder. func (c *Libx264Codec) GlobalFlags() string { return "" } +// PixelFormat is the pixel format required for this codec. func (c *Libx264Codec) PixelFormat() string { return "yuv420p" //nolint:goconst } +// ExtraArguments are the extra arguments used with this codec in the transcoder. func (c *Libx264Codec) ExtraArguments() string { return strings.Join([]string{ "-tune", "zerolatency", // Option used for good for fast encoding and low-latency streaming (always includes iframes in each segment) }, " ") } +// ExtraFilters are the extra filters required for this codec in the transcoder. func (c *Libx264Codec) ExtraFilters() string { return "" } +// VariantFlags returns a string representing a single variant processed by this codec. func (c *Libx264Codec) VariantFlags(v *HLSVariant) string { bufferSize := int(float64(v.videoBitrate) * 1.2) // How often it checks the bitrate of encoded segments to see if it's too high/low. @@ -68,6 +76,7 @@ func (c *Libx264Codec) VariantFlags(v *HLSVariant) string { }, " ") } +// GetPresetForLevel returns the string preset for this codec given an integer level. func (c *Libx264Codec) GetPresetForLevel(l int) string { presetMapping := []string{ "ultrafast", @@ -84,39 +93,48 @@ func (c *Libx264Codec) GetPresetForLevel(l int) string { return presetMapping[l] } +// OmxCodec represents an instance of the Omx codec. type OmxCodec struct { } +// Name returns the codec name. func (c *OmxCodec) Name() string { return "h264_omx" } +// DisplayName returns the human readable name of the codec. func (c *OmxCodec) DisplayName() string { return "OpenMAX (omx)" } +// GlobalFlags are the global flags used with this codec in the transcoder. func (c *OmxCodec) GlobalFlags() string { return "" } +// PixelFormat is the pixel format required for this codec. func (c *OmxCodec) PixelFormat() string { return "yuv420p" } +// ExtraArguments are the extra arguments used with this codec in the transcoder. func (c *OmxCodec) ExtraArguments() string { return strings.Join([]string{ "-tune", "zerolatency", // Option used for good for fast encoding and low-latency streaming (always includes iframes in each segment) }, " ") } +// ExtraFilters are the extra filters required for this codec in the transcoder. func (c *OmxCodec) ExtraFilters() string { return "" } +// VariantFlags returns a string representing a single variant processed by this codec. func (c *OmxCodec) VariantFlags(v *HLSVariant) string { return "" } +// GetPresetForLevel returns the string preset for this codec given an integer level. func (c *OmxCodec) GetPresetForLevel(l int) string { presetMapping := []string{ "ultrafast", @@ -133,17 +151,21 @@ func (c *OmxCodec) GetPresetForLevel(l int) string { return presetMapping[l] } +// VaapiCodec represents an instance of the Vaapi codec. type VaapiCodec struct { } +// Name returns the codec name. func (c *VaapiCodec) Name() string { return "h264_vaapi" } +// DisplayName returns the human readable name of the codec. func (c *VaapiCodec) DisplayName() string { return "VA-API" } +// GlobalFlags are the global flags used with this codec in the transcoder. func (c *VaapiCodec) GlobalFlags() string { flags := []string{ "-vaapi_device", "/dev/dri/renderD128", @@ -152,22 +174,27 @@ func (c *VaapiCodec) GlobalFlags() string { return strings.Join(flags, " ") } +// PixelFormat is the pixel format required for this codec. func (c *VaapiCodec) PixelFormat() string { return "vaapi_vld" } +// ExtraFilters are the extra filters required for this codec in the transcoder. func (c *VaapiCodec) ExtraFilters() string { return "format=nv12,hwupload" } +// ExtraArguments are the extra arguments used with this codec in the transcoder. func (c *VaapiCodec) ExtraArguments() string { return "" } +// VariantFlags returns a string representing a single variant processed by this codec. func (c *VaapiCodec) VariantFlags(v *HLSVariant) string { return "" } +// GetPresetForLevel returns the string preset for this codec given an integer level. func (c *VaapiCodec) GetPresetForLevel(l int) string { presetMapping := []string{ "ultrafast", @@ -184,17 +211,21 @@ func (c *VaapiCodec) GetPresetForLevel(l int) string { return presetMapping[l] } +// NvencCodec represents an instance of the Nvenc Codec. type NvencCodec struct { } +// Name returns the codec name. func (c *NvencCodec) Name() string { return "h264_nvenc" } +// DisplayName returns the human readable name of the codec. func (c *NvencCodec) DisplayName() string { return "nvidia nvenc" } +// GlobalFlags are the global flags used with this codec in the transcoder. func (c *NvencCodec) GlobalFlags() string { flags := []string{ "-hwaccel cuda", @@ -203,23 +234,28 @@ func (c *NvencCodec) GlobalFlags() string { return strings.Join(flags, " ") } +// PixelFormat is the pixel format required for this codec. func (c *NvencCodec) PixelFormat() string { return "yuv420p" } +// ExtraArguments are the extra arguments used with this codec in the transcoder. func (c *NvencCodec) ExtraArguments() string { return "" } +// ExtraFilters are the extra filters required for this codec in the transcoder. func (c *NvencCodec) ExtraFilters() string { return "" } +// VariantFlags returns a string representing a single variant processed by this codec. func (c *NvencCodec) VariantFlags(v *HLSVariant) string { tuning := "ll" // low latency return fmt.Sprintf("-tune:v:%d %s", v.index, tuning) } +// GetPresetForLevel returns the string preset for this codec given an integer level. func (c *NvencCodec) GetPresetForLevel(l int) string { presetMapping := []string{ "p1", @@ -236,37 +272,46 @@ func (c *NvencCodec) GetPresetForLevel(l int) string { return presetMapping[l] } +// QuicksyncCodec represents an instance of the Intel Quicksync Codec. type QuicksyncCodec struct { } +// Name returns the codec name. func (c *QuicksyncCodec) Name() string { return "h264_qsv" } +// DisplayName returns the human readable name of the codec. func (c *QuicksyncCodec) DisplayName() string { return "Intel QuickSync" } +// GlobalFlags are the global flags used with this codec in the transcoder. func (c *QuicksyncCodec) GlobalFlags() string { return "" } +// PixelFormat is the pixel format required for this codec. func (c *QuicksyncCodec) PixelFormat() string { return "nv12" } +// ExtraArguments are the extra arguments used with this codec in the transcoder. func (c *QuicksyncCodec) ExtraArguments() string { return "" } +// ExtraFilters are the extra filters required for this codec in the transcoder. func (c *QuicksyncCodec) ExtraFilters() string { return "" } +// VariantFlags returns a string representing a single variant processed by this codec. func (c *QuicksyncCodec) VariantFlags(v *HLSVariant) string { return "" } +// GetPresetForLevel returns the string preset for this codec given an integer level. func (c *QuicksyncCodec) GetPresetForLevel(l int) string { presetMapping := []string{ "ultrafast", @@ -283,36 +328,45 @@ func (c *QuicksyncCodec) GetPresetForLevel(l int) string { return presetMapping[l] } +// Video4Linux represents an instance of the V4L Codec. type Video4Linux struct{} +// Name returns the codec name. func (c *Video4Linux) Name() string { return "h264_v4l2m2m" } +// DisplayName returns the human readable name of the codec. func (c *Video4Linux) DisplayName() string { return "Video4Linux" } +// GlobalFlags are the global flags used with this codec in the transcoder. func (c *Video4Linux) GlobalFlags() string { return "" } +// PixelFormat is the pixel format required for this codec. func (c *Video4Linux) PixelFormat() string { return "nv21" } +// ExtraArguments are the extra arguments used with this codec in the transcoder. func (c *Video4Linux) ExtraArguments() string { return "" } +// ExtraFilters are the extra filters required for this codec in the transcoder. func (c *Video4Linux) ExtraFilters() string { return "" } +// VariantFlags returns a string representing a single variant processed by this codec. func (c *Video4Linux) VariantFlags(v *HLSVariant) string { return "" } +// GetPresetForLevel returns the string preset for this codec given an integer level. func (c *Video4Linux) GetPresetForLevel(l int) string { presetMapping := []string{ "ultrafast", diff --git a/core/transcoder/thumbnailGenerator.go b/core/transcoder/thumbnailGenerator.go index 0ce2cf7e9..21e6cd079 100644 --- a/core/transcoder/thumbnailGenerator.go +++ b/core/transcoder/thumbnailGenerator.go @@ -18,6 +18,7 @@ import ( var _timer *time.Ticker +// StopThumbnailGenerator will stop the periodic generating of a thumbnail from video. func StopThumbnailGenerator() { if _timer != nil { _timer.Stop() @@ -98,11 +99,11 @@ func fireThumbnailGenerator(segmentPath string, variantIndex int) error { ffmpegCmd := strings.Join(thumbnailCmdFlags, " ") if _, err := exec.Command("sh", "-c", ffmpegCmd).Output(); err != nil { return err - } else { - // rename temp file - if err := os.Rename(outputFileTemp, outputFile); err != nil { - log.Errorln(err) - } + } + + // rename temp file + if err := os.Rename(outputFileTemp, outputFile); err != nil { + log.Errorln(err) } // If YP support is enabled also create an animated GIF preview diff --git a/core/transcoder/transcoder.go b/core/transcoder/transcoder.go index af3c0d6d0..697ebc927 100644 --- a/core/transcoder/transcoder.go +++ b/core/transcoder/transcoder.go @@ -76,6 +76,7 @@ func (v *VideoSize) getString() string { return "" } +// Stop will stop the transcoder and kill all processing. func (t *Transcoder) Stop() { log.Traceln("Transcoder STOP requested.") err := _commandExec.Process.Kill() @@ -410,15 +411,17 @@ func (t *Transcoder) SetAppendToStream(append bool) { t.appendToStream = append } -// SetIdentifer enables appending a unique identifier to segment file name. +// SetIdentifier enables appending a unique identifier to segment file name. func (t *Transcoder) SetIdentifier(output string) { t.segmentIdentifier = output } +// SetInternalHTTPPort will set the port to be used for internal communication. func (t *Transcoder) SetInternalHTTPPort(port string) { t.internalListenerPort = port } +// SetCodec will set the codec to be used for the transocder. func (t *Transcoder) SetCodec(codecName string) { t.codec = getCodec(codecName) } diff --git a/core/transcoder/utils.go b/core/transcoder/utils.go index 7df9c2e66..a124002d9 100644 --- a/core/transcoder/utils.go +++ b/core/transcoder/utils.go @@ -99,24 +99,24 @@ func createVariantDirectories() { if len(data.GetStreamOutputVariants()) != 0 { for index := range data.GetStreamOutputVariants() { - if err := os.MkdirAll(path.Join(config.PrivateHLSStoragePath, strconv.Itoa(index)), 0777); err != nil { + if err := os.MkdirAll(path.Join(config.PrivateHLSStoragePath, strconv.Itoa(index)), 0750); err != nil { log.Fatalln(err) } dir := path.Join(config.PublicHLSStoragePath, strconv.Itoa(index)) log.Traceln("Creating", dir) - if err := os.MkdirAll(dir, 0777); err != nil { + if err := os.MkdirAll(dir, 0750); err != nil { log.Fatalln(err) } } } else { dir := path.Join(config.PrivateHLSStoragePath, strconv.Itoa(0)) log.Traceln("Creating", dir) - if err := os.MkdirAll(dir, 0777); err != nil { + if err := os.MkdirAll(dir, 0750); err != nil { log.Fatalln(err) } dir = path.Join(config.PublicHLSStoragePath, strconv.Itoa(0)) log.Traceln("Creating", dir) - if err := os.MkdirAll(dir, 0777); err != nil { + if err := os.MkdirAll(dir, 0750); err != nil { log.Fatalln(err) } } diff --git a/core/user/externalAPIUser.go b/core/user/externalAPIUser.go index 9bfeefccd..20f3732b8 100644 --- a/core/user/externalAPIUser.go +++ b/core/user/externalAPIUser.go @@ -14,7 +14,7 @@ import ( // ExternalAPIUser represents a single 3rd party integration that uses an access token. // This struct mostly matches the User struct so they can be used interchangeably. type ExternalAPIUser struct { - Id string `json:"id"` + ID string `json:"id"` AccessToken string `json:"accessToken"` DisplayName string `json:"displayName"` DisplayColor int `json:"displayColor"` @@ -40,7 +40,7 @@ var validAccessTokenScopes = []string{ ScopeHasAdminAccess, } -// InsertToken will add a new token to the database. +// InsertExternalAPIUser will add a new API user to the database. func InsertExternalAPIUser(token string, name string, color int, scopes []string) error { log.Traceln("Adding new API user:", name) @@ -206,7 +206,7 @@ func makeExternalAPIUserFromRow(row *sql.Row) (*ExternalAPIUser, error) { } integration := ExternalAPIUser{ - Id: id, + ID: id, AccessToken: accessToken, DisplayName: displayName, DisplayColor: displayColor, @@ -237,7 +237,7 @@ func makeExternalAPIUsersFromRows(rows *sql.Rows) ([]ExternalAPIUser, error) { } integration := ExternalAPIUser{ - Id: id, + ID: id, AccessToken: accessToken, DisplayName: displayName, DisplayColor: displayColor, diff --git a/core/user/user.go b/core/user/user.go index cb3e25707..62f92740a 100644 --- a/core/user/user.go +++ b/core/user/user.go @@ -16,8 +16,9 @@ import ( var _datastore *data.Datastore +// User represents a single chat user. type User struct { - Id string `json:"id"` + ID string `json:"id"` AccessToken string `json:"-"` DisplayName string `json:"displayName"` DisplayColor int `json:"displayColor"` @@ -27,14 +28,17 @@ type User struct { NameChangedAt *time.Time `json:"nameChangedAt,omitempty"` } +// IsEnabled will return if this single user is enabled. func (u *User) IsEnabled() bool { return u.DisabledAt == nil } +// SetupUsers will perform the initial initialization of the user package. func SetupUsers() { _datastore = data.GetDatastore() } +// CreateAnonymousUser will create a new anonymous user with the provided display name. func CreateAnonymousUser(username string) (*User, error) { id := shortid.MustGenerate() accessToken, err := utils.GenerateAccessToken() @@ -51,7 +55,7 @@ func CreateAnonymousUser(username string) (*User, error) { displayColor := utils.GenerateRandomDisplayColor() user := &User{ - Id: id, + ID: id, AccessToken: accessToken, DisplayName: displayName, DisplayColor: displayColor, @@ -65,7 +69,8 @@ func CreateAnonymousUser(username string) (*User, error) { return user, nil } -func ChangeUsername(userId string, username string) { +// ChangeUsername will change the user associated to userID from one display name to another. +func ChangeUsername(userID string, username string) { _datastore.DbLock.Lock() defer _datastore.DbLock.Unlock() @@ -87,13 +92,13 @@ func ChangeUsername(userId string, username string) { } defer stmt.Close() - _, err = stmt.Exec(username, fmt.Sprintf(",%s", username), time.Now(), userId) + _, err = stmt.Exec(username, fmt.Sprintf(",%s", username), time.Now(), userID) if err != nil { log.Errorln(err) } if err := tx.Commit(); err != nil { - log.Errorln("error changing display name of user", userId, err) + log.Errorln("error changing display name of user", userID, err) } } @@ -116,7 +121,7 @@ func create(user *User) error { } defer stmt.Close() - _, err = stmt.Exec(user.Id, user.AccessToken, user.DisplayName, user.DisplayColor, user.DisplayName, user.CreatedAt) + _, err = stmt.Exec(user.ID, user.AccessToken, user.DisplayName, user.DisplayColor, user.DisplayName, user.CreatedAt) if err != nil { log.Errorln("error creating new user", err) } @@ -124,6 +129,7 @@ func create(user *User) error { return tx.Commit() } +// SetEnabled will will set the enabled flag on a single user assigned to userID. func SetEnabled(userID string, enabled bool) error { _datastore.DbLock.Lock() defer _datastore.DbLock.Unlock() @@ -166,8 +172,8 @@ func GetUserByToken(token string) *User { return getUserFromRow(row) } -// GetUserById will return a user by a user ID. -func GetUserById(id string) *User { +// GetUserByID will return a user by a user ID. +func GetUserByID(id string) *User { _datastore.DbLock.Lock() defer _datastore.DbLock.Unlock() @@ -218,7 +224,7 @@ func getUsersFromRows(rows *sql.Rows) []*User { } user := &User{ - Id: id, + ID: id, DisplayName: displayName, DisplayColor: displayColor, CreatedAt: createdAt, @@ -250,7 +256,7 @@ func getUserFromRow(row *sql.Row) *User { } return &User{ - Id: id, + ID: id, DisplayName: displayName, DisplayColor: displayColor, CreatedAt: createdAt, diff --git a/core/webhooks/chat.go b/core/webhooks/chat.go index 0beb5ba63..019eb3cef 100644 --- a/core/webhooks/chat.go +++ b/core/webhooks/chat.go @@ -5,6 +5,7 @@ import ( "github.com/owncast/owncast/models" ) +// SendChatEvent will send a chat event to webhook destinations. func SendChatEvent(chatEvent *events.UserMessageEvent) { webhookEvent := WebhookEvent{ Type: chatEvent.GetMessageType(), @@ -12,7 +13,7 @@ func SendChatEvent(chatEvent *events.UserMessageEvent) { User: chatEvent.User, Body: chatEvent.Body, RawBody: chatEvent.RawBody, - ID: chatEvent.Id, + ID: chatEvent.ID, Visible: chatEvent.HiddenAt == nil, Timestamp: &chatEvent.Timestamp, }, @@ -21,6 +22,7 @@ func SendChatEvent(chatEvent *events.UserMessageEvent) { SendEventToWebhooks(webhookEvent) } +// SendChatEventUsernameChanged will send a username changed event to webhook destinations. func SendChatEventUsernameChanged(event events.NameChangeEvent) { webhookEvent := WebhookEvent{ Type: models.UserNameChanged, diff --git a/core/webhooks/stream.go b/core/webhooks/stream.go index be031e17e..c2b282445 100644 --- a/core/webhooks/stream.go +++ b/core/webhooks/stream.go @@ -8,6 +8,7 @@ import ( "github.com/teris-io/shortid" ) +// SendStreamStatusEvent will send all webhook destinations the current stream status. func SendStreamStatusEvent(eventType models.EventType) { SendEventToWebhooks(WebhookEvent{ Type: eventType, diff --git a/core/webhooks/webhooks.go b/core/webhooks/webhooks.go index 1c07fd6b8..057373c5a 100644 --- a/core/webhooks/webhooks.go +++ b/core/webhooks/webhooks.go @@ -14,11 +14,13 @@ import ( "github.com/owncast/owncast/models" ) +// WebhookEvent represents an event sent as a webhook. type WebhookEvent struct { Type models.EventType `json:"type"` // messageSent | userJoined | userNameChange EventData interface{} `json:"eventData,omitempty"` } +// WebhookChatMessage represents a single chat message sent as a webhook payload. type WebhookChatMessage struct { User *user.User `json:"user,omitempty"` Body string `json:"body,omitempty"` @@ -28,6 +30,7 @@ type WebhookChatMessage struct { Timestamp *time.Time `json:"timestamp,omitempty"` } +// SendEventToWebhooks will send a single webhook event to all webhook destinations. func SendEventToWebhooks(payload WebhookEvent) { webhooks := data.GetWebhooksForEvent(payload.Type) diff --git a/logging/logging.go b/logging/logging.go index 3cf0dad97..2d5b7d39c 100644 --- a/logging/logging.go +++ b/logging/logging.go @@ -19,12 +19,14 @@ import ( const maxLogEntries = 500 +// OCLogger represents the owncast internal logging. type OCLogger struct { Entries []logrus.Entry Warnings []logrus.Entry mu sync.RWMutex } +// Logger is the shared instance of the internal logger. var Logger *OCLogger // Setup configures our custom logging destinations. diff --git a/logging/paths.go b/logging/paths.go index 09c0b3593..31d05e902 100644 --- a/logging/paths.go +++ b/logging/paths.go @@ -6,6 +6,7 @@ import ( "github.com/owncast/owncast/config" ) +// GetTranscoderLogFilePath returns the logging path for the transcoder log output. func GetTranscoderLogFilePath() string { return filepath.Join(config.LogDirectory, "transcoder.log") } diff --git a/metrics/alerting.go b/metrics/alerting.go index 853adb955..58452de99 100644 --- a/metrics/alerting.go +++ b/metrics/alerting.go @@ -10,8 +10,8 @@ const maxCPUAlertingThresholdPCT = 85 const maxRAMAlertingThresholdPCT = 85 const maxDiskAlertingThresholdPCT = 90 -var inCpuAlertingState = false -var inRamAlertingState = false +var inCPUAlertingState = false +var inRAMAlertingState = false var inDiskAlertingState = false var errorResetDuration = time.Minute * 5 @@ -30,14 +30,14 @@ func handleCPUAlerting() { } avg := recentAverage(Metrics.CPUUtilizations) - if avg > maxCPUAlertingThresholdPCT && !inCpuAlertingState { + if avg > maxCPUAlertingThresholdPCT && !inCPUAlertingState { log.Warnf(alertingError, "CPU", avg) - inCpuAlertingState = true + inCPUAlertingState = true resetTimer := time.NewTimer(errorResetDuration) go func() { <-resetTimer.C - inCpuAlertingState = false + inCPUAlertingState = false }() } } @@ -48,14 +48,14 @@ func handleRAMAlerting() { } avg := recentAverage(Metrics.RAMUtilizations) - if avg > maxRAMAlertingThresholdPCT && !inRamAlertingState { + if avg > maxRAMAlertingThresholdPCT && !inRAMAlertingState { log.Warnf(alertingError, "memory", avg) - inRamAlertingState = true + inRAMAlertingState = true resetTimer := time.NewTimer(errorResetDuration) go func() { <-resetTimer.C - inRamAlertingState = false + inRAMAlertingState = false }() } } diff --git a/models/streamOutputVariant.go b/models/streamOutputVariant.go index 377f3175c..ac2828c71 100644 --- a/models/streamOutputVariant.go +++ b/models/streamOutputVariant.go @@ -85,9 +85,9 @@ func getBitrateString(bitrate int) string { } else if bitrate >= 1000 { if math.Mod(float64(bitrate), 1000) == 0 { return fmt.Sprintf("%dMbps", bitrate/1000.0) - } else { - return fmt.Sprintf("%.1fMbps", float32(bitrate)/1000.0) } + + return fmt.Sprintf("%.1fMbps", float32(bitrate)/1000.0) } return "" diff --git a/router/middleware/auth.go b/router/middleware/auth.go index adabc93c7..2b0db88c9 100644 --- a/router/middleware/auth.go +++ b/router/middleware/auth.go @@ -10,6 +10,7 @@ import ( log "github.com/sirupsen/logrus" ) +// ExternalAccessTokenHandlerFunc is a function that is called after validing access. type ExternalAccessTokenHandlerFunc func(user.ExternalAPIUser, http.ResponseWriter, *http.Request) // RequireAdminAuth wraps a handler requiring HTTP basic auth for it using the given diff --git a/router/router.go b/router/router.go index bc05fa48a..5b5c5c82b 100644 --- a/router/router.go +++ b/router/router.go @@ -234,12 +234,12 @@ func Start() error { port := config.WebServerPort ip := config.WebServerIP - ip_addr := net.ParseIP(ip) - if ip_addr == nil { + ipAddr := net.ParseIP(ip) + if ipAddr == nil { log.Fatalln("Invalid IP address", ip) } - log.Infof("Web server is listening on IP %s port %d.", ip_addr.String(), port) + log.Infof("Web server is listening on IP %s port %d.", ipAddr.String(), port) log.Infoln("The web admin interface is available at /admin.") - return http.ListenAndServe(fmt.Sprintf("%s:%d", ip_addr.String(), port), nil) + return http.ListenAndServe(fmt.Sprintf("%s:%d", ipAddr.String(), port), nil) } diff --git a/utils/accessTokens.go b/utils/accessTokens.go index 98990ce45..bb2c6d785 100644 --- a/utils/accessTokens.go +++ b/utils/accessTokens.go @@ -8,6 +8,7 @@ import ( const tokenLength = 32 +// GenerateAccessToken will generate and return an access token. func GenerateAccessToken() (string, error) { return generateRandomString(tokenLength) } diff --git a/utils/backup.go b/utils/backup.go index 508cdbd31..cb90eab40 100644 --- a/utils/backup.go +++ b/utils/backup.go @@ -12,15 +12,15 @@ import ( "os" "path/filepath" - _ "github.com/mattn/go-sqlite3" "github.com/schollz/sqlite3dump" log "github.com/sirupsen/logrus" ) +// Restore will attempt to restore the database using a specified backup file. func Restore(backupFile string, databaseFile string) error { log.Printf("Restoring database backup %s to %s", backupFile, databaseFile) - data, err := ioutil.ReadFile(backupFile) + data, err := ioutil.ReadFile(backupFile) // nolint if err != nil { return fmt.Errorf("Unable to read backup file %s", err) } @@ -38,7 +38,7 @@ func Restore(backupFile string, databaseFile string) error { defer gz.Close() - rawSql := b.String() + rawSQL := b.String() if _, err := os.Create(databaseFile); err != nil { return errors.New("Unable to write restored database") @@ -49,13 +49,14 @@ func Restore(backupFile string, databaseFile string) error { if err != nil { return err } - if _, err := db.Exec(rawSql); err != nil { + if _, err := db.Exec(rawSQL); err != nil { return err } return nil } +// Backup will backup the provided instance of the database to the specified file. func Backup(db *sql.DB, backupFile string) { log.Traceln("Backing up database to", backupFile) @@ -76,10 +77,10 @@ func Backup(db *sql.DB, backupFile string) { handleError(err) return } - out.Flush() + _ = out.Flush() // Create a new backup file - f, err := os.OpenFile(backupFile, os.O_WRONLY|os.O_CREATE, 0600) + f, err := os.OpenFile(backupFile, os.O_WRONLY|os.O_CREATE, 0600) // nolint if err != nil { handleError(err) return diff --git a/utils/nulltime.go b/utils/nulltime.go index 71df254a4..e65f539e0 100644 --- a/utils/nulltime.go +++ b/utils/nulltime.go @@ -6,6 +6,7 @@ import ( "time" ) +// NullTime is a custom nullable time for representing datetime. type NullTime struct { Time time.Time Valid bool // Valid is true if Time is not NULL @@ -25,6 +26,7 @@ func (nt NullTime) Value() (driver.Value, error) { return nt.Time, nil } +// MarshalJSON implements the JSON marshal function. func (nt NullTime) MarshalJSON() ([]byte, error) { if !nt.Valid { return []byte("null"), nil @@ -33,6 +35,7 @@ func (nt NullTime) MarshalJSON() ([]byte, error) { return []byte(val), nil } +// UnmarshalJSON implements the JSON unmarshal function. func (nt NullTime) UnmarshalJSON(data []byte) error { dateString := string(data) if dateString == "null" { @@ -45,6 +48,6 @@ func (nt NullTime) UnmarshalJSON(data []byte) error { return err } - nt.Time = parsedDateTime + nt.Time = parsedDateTime // nolint return nil } diff --git a/utils/phraseGenerator.go b/utils/phraseGenerator.go index dc0f2a20f..7f5aa7418 100644 --- a/utils/phraseGenerator.go +++ b/utils/phraseGenerator.go @@ -626,11 +626,12 @@ var ( } ) +// GeneratePhrase will generate and return a random string consisting of our word list. func GeneratePhrase() string { r := rand.New(rand.NewSource(time.Now().UnixNano())) //nolint - left_index := int(r.Float32() * float32(len(left))) - right_index := int(r.Float32() * float32(len(right))) + leftIndex := int(r.Float32() * float32(len(left))) + rightIndex := int(r.Float32() * float32(len(right))) - return fmt.Sprintf("%s-%s", left[left_index], right[right_index]) + return fmt.Sprintf("%s-%s", left[leftIndex], right[rightIndex]) } diff --git a/utils/utils.go b/utils/utils.go index 054fec779..14767cdac 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -42,6 +42,7 @@ func GetRelativePathFromAbsolutePath(path string) string { return filepath.Join(variant, file) } +// GetIndexFromFilePath is a utility that will return the index/key/variant name in a full path. func GetIndexFromFilePath(path string) string { pathComponents := strings.Split(path, "/") variant := pathComponents[len(pathComponents)-2] @@ -51,7 +52,7 @@ func GetIndexFromFilePath(path string) string { // Copy copies the file to destination. func Copy(source, destination string) error { - input, err := ioutil.ReadFile(source) + input, err := ioutil.ReadFile(source) // nolint if err != nil { return err } @@ -108,6 +109,7 @@ func IsUserAgentAPlayer(userAgent string) bool { return false } +// RenderSimpleMarkdown will return HTML without sanitization or specific formatting rules. func RenderSimpleMarkdown(raw string) string { markdown := goldmark.New( goldmark.WithRendererOptions( @@ -135,6 +137,7 @@ func RenderSimpleMarkdown(raw string) string { return buf.String() } +// RenderPageContentMarkdown will return HTML specifically handled for the user-specified page content. func RenderPageContentMarkdown(raw string) string { markdown := goldmark.New( goldmark.WithRendererOptions( @@ -189,7 +192,8 @@ func GetCacheDurationSecondsForPath(filePath string) int { return 60 * 10 } -func IsValidUrl(urlToTest string) bool { +// IsValidURL will return if a URL string is a valid URL or not. +func IsValidURL(urlToTest string) bool { if _, err := url.ParseRequestURI(urlToTest); err != nil { return false } @@ -207,9 +211,8 @@ func ValidatedFfmpegPath(ffmpegPath string) string { if ffmpegPath != "" { if err := VerifyFFMpegPath(ffmpegPath); err == nil { return ffmpegPath - } else { - log.Warnln(ffmpegPath, "is an invalid path to ffmpeg will try to use a copy in your path, if possible") } + log.Warnln(ffmpegPath, "is an invalid path to ffmpeg will try to use a copy in your path, if possible") } // First look to see if ffmpeg is in the current working directory @@ -255,17 +258,18 @@ func VerifyFFMpegPath(path string) error { return nil } -// Removes the directory and makes it again. Throws fatal error on failure. +// CleanupDirectory removes the directory and makes it fresh again. Throws fatal error on failure. func CleanupDirectory(path string) { log.Traceln("Cleaning", path) if err := os.RemoveAll(path); err != nil { log.Fatalln("Unable to remove directory. Please check the ownership and permissions", err) } - if err := os.MkdirAll(path, 0777); err != nil { + if err := os.MkdirAll(path, 0750); err != nil { log.Fatalln("Unable to create directory. Please check the ownership and permissions", err) } } +// FindInSlice will return the index if a string is located in a slice of strings. func FindInSlice(slice []string, val string) (int, bool) { for i, item := range slice { if item == val { diff --git a/yp/yp.go b/yp/yp.go index 1b3464ee0..2c82247bb 100644 --- a/yp/yp.go +++ b/yp/yp.go @@ -75,7 +75,7 @@ func (yp *YP) ping() { log.Warnln("Server URL not set in the configuration. Directory access is disabled until this is set.") return } - isValidInstanceURL := isUrl(myInstanceURL) + isValidInstanceURL := isURL(myInstanceURL) if myInstanceURL == "" || !isValidInstanceURL { if !_inErrorState { log.Warnln("YP Error: unable to use", myInstanceURL, "as a public instance URL. Fix this value in your configuration.") @@ -134,7 +134,7 @@ func (yp *YP) ping() { } } -func isUrl(str string) bool { +func isURL(str string) bool { u, err := url.Parse(str) return err == nil && u.Scheme != "" && u.Host != "" }