diff --git a/docs/docs.go b/docs/docs.go index f14469e8..3a016149 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -109,6 +109,76 @@ const docTemplate = `{ } } }, + "/api/login": { + "post": { + "description": "Create a JWT from username/password", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "summary": "Create a JWT", + "operationId": "login", + "parameters": [ + { + "description": "User definition", + "name": "user", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/api.Login" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/api.JWT" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/api.Error" + } + } + } + } + }, + "/api/login/refresh": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Refresh a JWT", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "summary": "Refresh a JWT", + "operationId": "refresh", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/api.JWTRefresh" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/api.Error" + } + } + } + } + }, "/api/swagger": { "get": { "description": "Swagger UI for this API", @@ -488,7 +558,7 @@ const docTemplate = `{ "ApiKeyAuth": [] } ], - "description": "Stream of events of whats happening on each node in the cluster", + "description": "Stream of log events of whats happening on each node in the cluster", "consumes": [ "application/json" ], @@ -499,7 +569,7 @@ const docTemplate = `{ "tags": [ "v16.?.?" ], - "summary": "Stream of events", + "summary": "Stream of log events", "operationId": "cluster-3-events", "parameters": [ { @@ -515,7 +585,7 @@ const docTemplate = `{ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/api.Event" + "$ref": "#/definitions/api.LogEvent" } } } @@ -2500,7 +2570,7 @@ const docTemplate = `{ "ApiKeyAuth": [] } ], - "description": "Stream of event of whats happening in the core", + "description": "Stream of log event of whats happening in the core", "consumes": [ "application/json" ], @@ -2511,8 +2581,8 @@ const docTemplate = `{ "tags": [ "v16.?.?" ], - "summary": "Stream of events", - "operationId": "events", + "summary": "Stream of log events", + "operationId": "events-3-media", "parameters": [ { "description": "Event filters", @@ -2527,7 +2597,44 @@ const docTemplate = `{ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/api.Event" + "$ref": "#/definitions/api.MediaEvent" + } + } + } + } + }, + "/api/v3/events/media/{type}": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Stream of media event of whats happening in the core", + "consumes": [ + "application/json" + ], + "produces": [ + "application/x-json-stream" + ], + "tags": [ + "v16.?.?" + ], + "summary": "Stream of media events", + "operationId": "events-3-log", + "parameters": [ + { + "type": "string", + "description": "glob pattern for media names", + "name": "glob", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/api.LogEvent" } } } @@ -4986,8 +5093,7 @@ const docTemplate = `{ ], "description": "List all currently publishing RTMP streams.", "produces": [ - "application/json", - "application/x-json-stream" + "application/json" ], "tags": [ "v16.7.2" @@ -5207,8 +5313,7 @@ const docTemplate = `{ ], "description": "List all currently publishing SRT streams. This endpoint is EXPERIMENTAL and may change in future.", "produces": [ - "application/json", - "application/x-json-stream" + "application/json" ], "tags": [ "v16.9.0" @@ -6670,69 +6775,13 @@ const docTemplate = `{ } } }, - "api.Event": { - "type": "object", - "properties": { - "caller": { - "type": "string" - }, - "core_id": { - "type": "string" - }, - "data": { - "type": "object", - "additionalProperties": { - "type": "string" - } - }, - "event": { - "type": "string" - }, - "level": { - "type": "integer" - }, - "message": { - "type": "string" - }, - "ts": { - "type": "integer", - "format": "int64" - } - } - }, - "api.EventFilter": { - "type": "object", - "properties": { - "caller": { - "type": "string" - }, - "core_id": { - "type": "string" - }, - "data": { - "type": "object", - "additionalProperties": { - "type": "string" - } - }, - "event": { - "type": "string" - }, - "level": { - "type": "string" - }, - "message": { - "type": "string" - } - } - }, "api.EventFilters": { "type": "object", "properties": { "filters": { "type": "array", "items": { - "$ref": "#/definitions/api.EventFilter" + "$ref": "#/definitions/api.LogEventFilter" } } } @@ -7042,10 +7091,120 @@ const docTemplate = `{ } } }, - "api.LogEvent": { + "api.JWT": { + "type": "object", + "properties": { + "access_token": { + "type": "string" + }, + "refresh_token": { + "type": "string" + } + } + }, + "api.JWTRefresh": { + "type": "object", + "properties": { + "access_token": { + "type": "string" + } + } + }, + "api.LogEntries": { "type": "object", "additionalProperties": true }, + "api.LogEvent": { + "type": "object", + "properties": { + "caller": { + "type": "string" + }, + "core_id": { + "type": "string" + }, + "data": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "event": { + "type": "string" + }, + "level": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "ts": { + "type": "integer", + "format": "int64" + } + } + }, + "api.LogEventFilter": { + "type": "object", + "properties": { + "caller": { + "type": "string" + }, + "core_id": { + "type": "string" + }, + "data": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "event": { + "type": "string" + }, + "level": { + "type": "string" + }, + "message": { + "type": "string" + } + } + }, + "api.Login": { + "type": "object", + "required": [ + "password", + "username" + ], + "properties": { + "password": { + "type": "string" + }, + "username": { + "type": "string" + } + } + }, + "api.MediaEvent": { + "type": "object", + "properties": { + "action": { + "type": "string" + }, + "name": { + "type": "string" + }, + "names": { + "type": "array", + "items": { + "type": "string" + } + }, + "ts": { + "type": "integer" + } + } + }, "api.MetricsDescription": { "type": "object", "properties": { diff --git a/docs/swagger.json b/docs/swagger.json index dd940a30..4a4dd00e 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -102,6 +102,76 @@ } } }, + "/api/login": { + "post": { + "description": "Create a JWT from username/password", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "summary": "Create a JWT", + "operationId": "login", + "parameters": [ + { + "description": "User definition", + "name": "user", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/api.Login" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/api.JWT" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/api.Error" + } + } + } + } + }, + "/api/login/refresh": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Refresh a JWT", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "summary": "Refresh a JWT", + "operationId": "refresh", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/api.JWTRefresh" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/api.Error" + } + } + } + } + }, "/api/swagger": { "get": { "description": "Swagger UI for this API", @@ -481,7 +551,7 @@ "ApiKeyAuth": [] } ], - "description": "Stream of events of whats happening on each node in the cluster", + "description": "Stream of log events of whats happening on each node in the cluster", "consumes": [ "application/json" ], @@ -492,7 +562,7 @@ "tags": [ "v16.?.?" ], - "summary": "Stream of events", + "summary": "Stream of log events", "operationId": "cluster-3-events", "parameters": [ { @@ -508,7 +578,7 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/api.Event" + "$ref": "#/definitions/api.LogEvent" } } } @@ -2493,7 +2563,7 @@ "ApiKeyAuth": [] } ], - "description": "Stream of event of whats happening in the core", + "description": "Stream of log event of whats happening in the core", "consumes": [ "application/json" ], @@ -2504,8 +2574,8 @@ "tags": [ "v16.?.?" ], - "summary": "Stream of events", - "operationId": "events", + "summary": "Stream of log events", + "operationId": "events-3-media", "parameters": [ { "description": "Event filters", @@ -2520,7 +2590,44 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/api.Event" + "$ref": "#/definitions/api.MediaEvent" + } + } + } + } + }, + "/api/v3/events/media/{type}": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Stream of media event of whats happening in the core", + "consumes": [ + "application/json" + ], + "produces": [ + "application/x-json-stream" + ], + "tags": [ + "v16.?.?" + ], + "summary": "Stream of media events", + "operationId": "events-3-log", + "parameters": [ + { + "type": "string", + "description": "glob pattern for media names", + "name": "glob", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/api.LogEvent" } } } @@ -4979,8 +5086,7 @@ ], "description": "List all currently publishing RTMP streams.", "produces": [ - "application/json", - "application/x-json-stream" + "application/json" ], "tags": [ "v16.7.2" @@ -5200,8 +5306,7 @@ ], "description": "List all currently publishing SRT streams. This endpoint is EXPERIMENTAL and may change in future.", "produces": [ - "application/json", - "application/x-json-stream" + "application/json" ], "tags": [ "v16.9.0" @@ -6663,69 +6768,13 @@ } } }, - "api.Event": { - "type": "object", - "properties": { - "caller": { - "type": "string" - }, - "core_id": { - "type": "string" - }, - "data": { - "type": "object", - "additionalProperties": { - "type": "string" - } - }, - "event": { - "type": "string" - }, - "level": { - "type": "integer" - }, - "message": { - "type": "string" - }, - "ts": { - "type": "integer", - "format": "int64" - } - } - }, - "api.EventFilter": { - "type": "object", - "properties": { - "caller": { - "type": "string" - }, - "core_id": { - "type": "string" - }, - "data": { - "type": "object", - "additionalProperties": { - "type": "string" - } - }, - "event": { - "type": "string" - }, - "level": { - "type": "string" - }, - "message": { - "type": "string" - } - } - }, "api.EventFilters": { "type": "object", "properties": { "filters": { "type": "array", "items": { - "$ref": "#/definitions/api.EventFilter" + "$ref": "#/definitions/api.LogEventFilter" } } } @@ -7035,10 +7084,120 @@ } } }, - "api.LogEvent": { + "api.JWT": { + "type": "object", + "properties": { + "access_token": { + "type": "string" + }, + "refresh_token": { + "type": "string" + } + } + }, + "api.JWTRefresh": { + "type": "object", + "properties": { + "access_token": { + "type": "string" + } + } + }, + "api.LogEntries": { "type": "object", "additionalProperties": true }, + "api.LogEvent": { + "type": "object", + "properties": { + "caller": { + "type": "string" + }, + "core_id": { + "type": "string" + }, + "data": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "event": { + "type": "string" + }, + "level": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "ts": { + "type": "integer", + "format": "int64" + } + } + }, + "api.LogEventFilter": { + "type": "object", + "properties": { + "caller": { + "type": "string" + }, + "core_id": { + "type": "string" + }, + "data": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "event": { + "type": "string" + }, + "level": { + "type": "string" + }, + "message": { + "type": "string" + } + } + }, + "api.Login": { + "type": "object", + "required": [ + "password", + "username" + ], + "properties": { + "password": { + "type": "string" + }, + "username": { + "type": "string" + } + } + }, + "api.MediaEvent": { + "type": "object", + "properties": { + "action": { + "type": "string" + }, + "name": { + "type": "string" + }, + "names": { + "type": "array", + "items": { + "type": "string" + } + }, + "ts": { + "type": "integer" + } + } + }, "api.MetricsDescription": { "type": "object", "properties": { diff --git a/docs/swagger.yaml b/docs/swagger.yaml index aec0ad13..fe57bb18 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -911,48 +911,11 @@ definitions: message: type: string type: object - api.Event: - properties: - caller: - type: string - core_id: - type: string - data: - additionalProperties: - type: string - type: object - event: - type: string - level: - type: integer - message: - type: string - ts: - format: int64 - type: integer - type: object - api.EventFilter: - properties: - caller: - type: string - core_id: - type: string - data: - additionalProperties: - type: string - type: object - event: - type: string - level: - type: string - message: - type: string - type: object api.EventFilters: properties: filters: items: - $ref: '#/definitions/api.EventFilter' + $ref: '#/definitions/api.LogEventFilter' type: array type: object api.FileInfo: @@ -1157,9 +1120,81 @@ definitions: type: string type: array type: object - api.LogEvent: + api.JWT: + properties: + access_token: + type: string + refresh_token: + type: string + type: object + api.JWTRefresh: + properties: + access_token: + type: string + type: object + api.LogEntries: additionalProperties: true type: object + api.LogEvent: + properties: + caller: + type: string + core_id: + type: string + data: + additionalProperties: + type: string + type: object + event: + type: string + level: + type: integer + message: + type: string + ts: + format: int64 + type: integer + type: object + api.LogEventFilter: + properties: + caller: + type: string + core_id: + type: string + data: + additionalProperties: + type: string + type: object + event: + type: string + level: + type: string + message: + type: string + type: object + api.Login: + properties: + password: + type: string + username: + type: string + required: + - password + - username + type: object + api.MediaEvent: + properties: + action: + type: string + name: + type: string + names: + items: + type: string + type: array + ts: + type: integer + type: object api.MetricsDescription: properties: description: @@ -2973,6 +3008,51 @@ paths: security: - ApiKeyAuth: [] summary: Query the GraphAPI + /api/login: + post: + consumes: + - application/json + description: Create a JWT from username/password + operationId: login + parameters: + - description: User definition + in: body + name: user + required: true + schema: + $ref: '#/definitions/api.Login' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/api.JWT' + "403": + description: Forbidden + schema: + $ref: '#/definitions/api.Error' + summary: Create a JWT + /api/login/refresh: + get: + consumes: + - application/json + description: Refresh a JWT + operationId: refresh + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/api.JWTRefresh' + "403": + description: Forbidden + schema: + $ref: '#/definitions/api.Error' + security: + - ApiKeyAuth: [] + summary: Refresh a JWT /api/swagger: get: description: Swagger UI for this API @@ -3209,7 +3289,7 @@ paths: post: consumes: - application/json - description: Stream of events of whats happening on each node in the cluster + description: Stream of log events of whats happening on each node in the cluster operationId: cluster-3-events parameters: - description: Event filters @@ -3224,10 +3304,10 @@ paths: "200": description: OK schema: - $ref: '#/definitions/api.Event' + $ref: '#/definitions/api.LogEvent' security: - ApiKeyAuth: [] - summary: Stream of events + summary: Stream of log events tags: - v16.?.? /api/v3/cluster/fs/{storage}: @@ -4531,8 +4611,8 @@ paths: post: consumes: - application/json - description: Stream of event of whats happening in the core - operationId: events + description: Stream of log event of whats happening in the core + operationId: events-3-media parameters: - description: Event filters in: body @@ -4546,10 +4626,33 @@ paths: "200": description: OK schema: - $ref: '#/definitions/api.Event' + $ref: '#/definitions/api.MediaEvent' security: - ApiKeyAuth: [] - summary: Stream of events + summary: Stream of log events + tags: + - v16.?.? + /api/v3/events/media/{type}: + post: + consumes: + - application/json + description: Stream of media event of whats happening in the core + operationId: events-3-log + parameters: + - description: glob pattern for media names + in: query + name: glob + type: string + produces: + - application/x-json-stream + responses: + "200": + description: OK + schema: + $ref: '#/definitions/api.LogEvent' + security: + - ApiKeyAuth: [] + summary: Stream of media events tags: - v16.?.? /api/v3/fs: @@ -6177,7 +6280,6 @@ paths: operationId: rtmp-3-list-channels produces: - application/json - - application/x-json-stream responses: "200": description: OK @@ -6318,7 +6420,6 @@ paths: operationId: srt-3-list-channels produces: - application/json - - application/x-json-stream responses: "200": description: OK diff --git a/http/handler/api/cluster_events.go b/http/handler/api/cluster_events.go index ae34899c..471ae5ac 100644 --- a/http/handler/api/cluster_events.go +++ b/http/handler/api/cluster_events.go @@ -13,16 +13,16 @@ import ( "github.com/labstack/echo/v4" ) -// Events returns a stream of event -// @Summary Stream of events -// @Description Stream of events of whats happening on each node in the cluster +// Events returns a stream of log event +// @Summary Stream of log events +// @Description Stream of log events of whats happening on each node in the cluster // @ID cluster-3-events // @Tags v16.?.? // @Accept json // @Produce text/event-stream // @Produce json-stream // @Param filters body api.EventFilters false "Event filters" -// @Success 200 {object} api.Event +// @Success 200 {object} api.LogEvent // @Security ApiKeyAuth // @Router /api/v3/cluster/events [post] func (h *ClusterHandler) Events(c echo.Context) error { diff --git a/http/handler/api/events.go b/http/handler/api/events.go index f0036d34..15e7cd22 100644 --- a/http/handler/api/events.go +++ b/http/handler/api/events.go @@ -46,13 +46,13 @@ func (h *EventsHandler) AddMediaSource(name string, source event.MediaSource) { // LogEvents returns a stream of event // @Summary Stream of log events // @Description Stream of log event of whats happening in the core -// @ID events +// @ID events-3-media // @Tags v16.?.? // @Accept json // @Produce text/event-stream // @Produce json-stream // @Param filters body api.EventFilters false "Event filters" -// @Success 200 {object} api.Event +// @Success 200 {object} api.MediaEvent // @Security ApiKeyAuth // @Router /api/v3/events [post] func (h *EventsHandler) LogEvents(c echo.Context) error { @@ -177,12 +177,12 @@ func (h *EventsHandler) LogEvents(c echo.Context) error { // LogEvents returns a stream of media event // @Summary Stream of media events // @Description Stream of media event of whats happening in the core -// @ID events +// @ID events-3-log // @Tags v16.?.? // @Accept json // @Param glob query string false "glob pattern for media names" // @Produce json-stream -// @Success 200 {object} api.Event +// @Success 200 {object} api.LogEvent // @Security ApiKeyAuth // @Router /api/v3/events/media/{type} [post] func (h *EventsHandler) MediaEvents(c echo.Context) error { diff --git a/http/handler/api/jwt.go b/http/handler/api/jwt.go index b7565470..7702490d 100644 --- a/http/handler/api/jwt.go +++ b/http/handler/api/jwt.go @@ -19,6 +19,16 @@ func NewJWT(iam iam.IAM) *JWTHandler { } } +// Login Creates a JWT from username/password +// @Summary Create a JWT +// @Description Create a JWT from username/password +// @ID login +// @Accept json +// @Produce json +// @Param user body api.Login true "User definition" +// @Success 200 {object} api.JWT +// @Failure 403 {object} api.Error +// @Router /api/login [post] func (j *JWTHandler) Login(c echo.Context) error { subject, ok := c.Get("user").(string) if !ok { @@ -36,6 +46,16 @@ func (j *JWTHandler) Login(c echo.Context) error { }) } +// Refresh Refreshes a JWT from refresh JWT +// @Summary Refresh a JWT +// @Description Refresh a JWT +// @ID refresh +// @Accept json +// @Produce json +// @Success 200 {object} api.JWTRefresh +// @Failure 403 {object} api.Error +// @Security ApiKeyAuth +// @Router /api/login/refresh [get] func (j *JWTHandler) Refresh(c echo.Context) error { subject, ok := c.Get("user").(string) if !ok { diff --git a/log/log.go b/log/log.go index a0965eb9..bc06e057 100644 --- a/log/log.go +++ b/log/log.go @@ -6,7 +6,9 @@ import ( "reflect" "runtime" "runtime/debug" + "slices" "strings" + "sync" "time" "github.com/datarhei/core/v16/encoding/json" @@ -15,6 +17,31 @@ import ( // LogLevel represents a log level type Level uint +var components = []string{} +var lock = sync.Mutex{} + +func registerComponent(component string) { + if len(component) == 0 { + return + } + + lock.Lock() + defer lock.Unlock() + + if slices.Contains(components, component) { + return + } + + components = append(components, component) +} + +func ListComponents() []string { + lock.Lock() + defer lock.Unlock() + + return slices.Clone(components) +} + const ( Lsilent Level = 0 Lerror Level = 1 @@ -108,6 +135,8 @@ type logger struct { // New returns an implementation of the Logger interface. func New(component string) Logger { + registerComponent(component) + l := &logger{ component: component, } @@ -156,6 +185,8 @@ func (l *logger) WithComponent(component string) Logger { clone := l.clone() clone.component = component + registerComponent(component) + return clone } @@ -219,9 +250,10 @@ func (e *Event) WithOutput(w Writer) Logger { func (e *Event) WithComponent(component string) Logger { clone := e.clone() - clone.Component = component + registerComponent(component) + return clone }