mirror of
https://github.com/kerberos-io/agent.git
synced 2025-10-23 16:23:06 +08:00
update dashboard page, add breadcrumb buttons
This commit is contained in:
@@ -24,7 +24,382 @@ const docTemplate = `{
|
|||||||
},
|
},
|
||||||
"host": "{{.Host}}",
|
"host": "{{.Host}}",
|
||||||
"basePath": "{{.BasePath}}",
|
"basePath": "{{.BasePath}}",
|
||||||
"paths": {},
|
"paths": {
|
||||||
|
"/api/hub/verify": {
|
||||||
|
"post": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"Bearer": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Will verify the hub connectivity.",
|
||||||
|
"tags": [
|
||||||
|
"config"
|
||||||
|
],
|
||||||
|
"summary": "Will verify the hub connectivity.",
|
||||||
|
"operationId": "verify-hub",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"description": "Config",
|
||||||
|
"name": "config",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/models.Config"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/models.APIResponse"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/api/persistence/verify": {
|
||||||
|
"post": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"Bearer": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Will verify the persistence.",
|
||||||
|
"tags": [
|
||||||
|
"config"
|
||||||
|
],
|
||||||
|
"summary": "Will verify the persistence.",
|
||||||
|
"operationId": "verify-persistence",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"description": "Config",
|
||||||
|
"name": "config",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/models.Config"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/models.APIResponse"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"definitions": {
|
||||||
|
"models.APIResponse": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"data": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"models.Capture": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"continuous": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"forwardwebrtc": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"fragmented": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"fragmentedduration": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"ipcamera": {
|
||||||
|
"$ref": "#/definitions/models.IPCamera"
|
||||||
|
},
|
||||||
|
"maxlengthrecording": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"pixelChangeThreshold": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"postrecording": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"prerecording": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"raspicamera": {
|
||||||
|
"$ref": "#/definitions/models.RaspiCamera"
|
||||||
|
},
|
||||||
|
"transcodingresolution": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"transcodingwebrtc": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"usbcamera": {
|
||||||
|
"$ref": "#/definitions/models.USBCamera"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"models.Config": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"capture": {
|
||||||
|
"$ref": "#/definitions/models.Capture"
|
||||||
|
},
|
||||||
|
"cloud": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"condition_uri": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"heartbeaturi": {
|
||||||
|
"description": "obsolete",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"hub_key": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"hub_private_key": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"hub_site": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"hub_uri": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"key": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"kstorage": {
|
||||||
|
"$ref": "#/definitions/models.KStorage"
|
||||||
|
},
|
||||||
|
"mqtt_password": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"mqtt_username": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"mqtturi": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"offline": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"region": {
|
||||||
|
"$ref": "#/definitions/models.Region"
|
||||||
|
},
|
||||||
|
"s3": {
|
||||||
|
"$ref": "#/definitions/models.S3"
|
||||||
|
},
|
||||||
|
"stunuri": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"time": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"timetable": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/models.Timetable"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"timezone": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"turn_password": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"turn_username": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"turnuri": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"type": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"models.Coordinate": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"x": {
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"y": {
|
||||||
|
"type": "number"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"models.IPCamera": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"fps": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"onvif": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"onvif_password": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"onvif_username": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"onvif_xaddr": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"rtsp": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"sub_rtsp": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"models.KStorage": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"access_key": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"cloud_key": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"directory": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"provider": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"secret_access_key": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"uri": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"models.Polygon": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"coordinates": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/models.Coordinate"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"id": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"models.RaspiCamera": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"device": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"models.Rectangle": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"x1": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"x2": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"y1": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"y2": {
|
||||||
|
"type": "integer"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"models.Region": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"name": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"polygon": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/models.Polygon"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"rectangle": {
|
||||||
|
"$ref": "#/definitions/models.Rectangle"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"models.S3": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"bucket": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"proxy": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"proxyuri": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"publickey": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"region": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"secretkey": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"username": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"models.Timetable": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"end1": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"end2": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"start1": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"start2": {
|
||||||
|
"type": "integer"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"models.USBCamera": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"device": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"securityDefinitions": {
|
"securityDefinitions": {
|
||||||
"Bearer": {
|
"Bearer": {
|
||||||
"type": "apiKey",
|
"type": "apiKey",
|
||||||
|
@@ -16,7 +16,382 @@
|
|||||||
"version": "1.0"
|
"version": "1.0"
|
||||||
},
|
},
|
||||||
"basePath": "/",
|
"basePath": "/",
|
||||||
"paths": {},
|
"paths": {
|
||||||
|
"/api/hub/verify": {
|
||||||
|
"post": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"Bearer": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Will verify the hub connectivity.",
|
||||||
|
"tags": [
|
||||||
|
"config"
|
||||||
|
],
|
||||||
|
"summary": "Will verify the hub connectivity.",
|
||||||
|
"operationId": "verify-hub",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"description": "Config",
|
||||||
|
"name": "config",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/models.Config"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/models.APIResponse"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/api/persistence/verify": {
|
||||||
|
"post": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"Bearer": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Will verify the persistence.",
|
||||||
|
"tags": [
|
||||||
|
"config"
|
||||||
|
],
|
||||||
|
"summary": "Will verify the persistence.",
|
||||||
|
"operationId": "verify-persistence",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"description": "Config",
|
||||||
|
"name": "config",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/models.Config"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/models.APIResponse"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"definitions": {
|
||||||
|
"models.APIResponse": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"data": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"models.Capture": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"continuous": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"forwardwebrtc": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"fragmented": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"fragmentedduration": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"ipcamera": {
|
||||||
|
"$ref": "#/definitions/models.IPCamera"
|
||||||
|
},
|
||||||
|
"maxlengthrecording": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"pixelChangeThreshold": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"postrecording": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"prerecording": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"raspicamera": {
|
||||||
|
"$ref": "#/definitions/models.RaspiCamera"
|
||||||
|
},
|
||||||
|
"transcodingresolution": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"transcodingwebrtc": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"usbcamera": {
|
||||||
|
"$ref": "#/definitions/models.USBCamera"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"models.Config": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"capture": {
|
||||||
|
"$ref": "#/definitions/models.Capture"
|
||||||
|
},
|
||||||
|
"cloud": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"condition_uri": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"heartbeaturi": {
|
||||||
|
"description": "obsolete",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"hub_key": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"hub_private_key": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"hub_site": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"hub_uri": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"key": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"kstorage": {
|
||||||
|
"$ref": "#/definitions/models.KStorage"
|
||||||
|
},
|
||||||
|
"mqtt_password": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"mqtt_username": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"mqtturi": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"offline": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"region": {
|
||||||
|
"$ref": "#/definitions/models.Region"
|
||||||
|
},
|
||||||
|
"s3": {
|
||||||
|
"$ref": "#/definitions/models.S3"
|
||||||
|
},
|
||||||
|
"stunuri": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"time": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"timetable": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/models.Timetable"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"timezone": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"turn_password": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"turn_username": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"turnuri": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"type": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"models.Coordinate": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"x": {
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"y": {
|
||||||
|
"type": "number"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"models.IPCamera": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"fps": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"onvif": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"onvif_password": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"onvif_username": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"onvif_xaddr": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"rtsp": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"sub_rtsp": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"models.KStorage": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"access_key": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"cloud_key": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"directory": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"provider": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"secret_access_key": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"uri": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"models.Polygon": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"coordinates": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/models.Coordinate"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"id": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"models.RaspiCamera": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"device": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"models.Rectangle": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"x1": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"x2": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"y1": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"y2": {
|
||||||
|
"type": "integer"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"models.Region": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"name": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"polygon": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/models.Polygon"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"rectangle": {
|
||||||
|
"$ref": "#/definitions/models.Rectangle"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"models.S3": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"bucket": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"proxy": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"proxyuri": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"publickey": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"region": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"secretkey": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"username": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"models.Timetable": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"end1": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"end2": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"start1": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"start2": {
|
||||||
|
"type": "integer"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"models.USBCamera": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"device": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"securityDefinitions": {
|
"securityDefinitions": {
|
||||||
"Bearer": {
|
"Bearer": {
|
||||||
"type": "apiKey",
|
"type": "apiKey",
|
||||||
|
@@ -1,4 +1,204 @@
|
|||||||
basePath: /
|
basePath: /
|
||||||
|
definitions:
|
||||||
|
models.APIResponse:
|
||||||
|
properties:
|
||||||
|
data: {}
|
||||||
|
type: object
|
||||||
|
models.Capture:
|
||||||
|
properties:
|
||||||
|
continuous:
|
||||||
|
type: string
|
||||||
|
forwardwebrtc:
|
||||||
|
type: string
|
||||||
|
fragmented:
|
||||||
|
type: string
|
||||||
|
fragmentedduration:
|
||||||
|
type: integer
|
||||||
|
ipcamera:
|
||||||
|
$ref: '#/definitions/models.IPCamera'
|
||||||
|
maxlengthrecording:
|
||||||
|
type: integer
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
pixelChangeThreshold:
|
||||||
|
type: integer
|
||||||
|
postrecording:
|
||||||
|
type: integer
|
||||||
|
prerecording:
|
||||||
|
type: integer
|
||||||
|
raspicamera:
|
||||||
|
$ref: '#/definitions/models.RaspiCamera'
|
||||||
|
transcodingresolution:
|
||||||
|
type: integer
|
||||||
|
transcodingwebrtc:
|
||||||
|
type: string
|
||||||
|
usbcamera:
|
||||||
|
$ref: '#/definitions/models.USBCamera'
|
||||||
|
type: object
|
||||||
|
models.Config:
|
||||||
|
properties:
|
||||||
|
capture:
|
||||||
|
$ref: '#/definitions/models.Capture'
|
||||||
|
cloud:
|
||||||
|
type: string
|
||||||
|
condition_uri:
|
||||||
|
type: string
|
||||||
|
heartbeaturi:
|
||||||
|
description: obsolete
|
||||||
|
type: string
|
||||||
|
hub_key:
|
||||||
|
type: string
|
||||||
|
hub_private_key:
|
||||||
|
type: string
|
||||||
|
hub_site:
|
||||||
|
type: string
|
||||||
|
hub_uri:
|
||||||
|
type: string
|
||||||
|
key:
|
||||||
|
type: string
|
||||||
|
kstorage:
|
||||||
|
$ref: '#/definitions/models.KStorage'
|
||||||
|
mqtt_password:
|
||||||
|
type: string
|
||||||
|
mqtt_username:
|
||||||
|
type: string
|
||||||
|
mqtturi:
|
||||||
|
type: string
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
offline:
|
||||||
|
type: string
|
||||||
|
region:
|
||||||
|
$ref: '#/definitions/models.Region'
|
||||||
|
s3:
|
||||||
|
$ref: '#/definitions/models.S3'
|
||||||
|
stunuri:
|
||||||
|
type: string
|
||||||
|
time:
|
||||||
|
type: string
|
||||||
|
timetable:
|
||||||
|
items:
|
||||||
|
$ref: '#/definitions/models.Timetable'
|
||||||
|
type: array
|
||||||
|
timezone:
|
||||||
|
type: string
|
||||||
|
turn_password:
|
||||||
|
type: string
|
||||||
|
turn_username:
|
||||||
|
type: string
|
||||||
|
turnuri:
|
||||||
|
type: string
|
||||||
|
type:
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
|
models.Coordinate:
|
||||||
|
properties:
|
||||||
|
x:
|
||||||
|
type: number
|
||||||
|
"y":
|
||||||
|
type: number
|
||||||
|
type: object
|
||||||
|
models.IPCamera:
|
||||||
|
properties:
|
||||||
|
fps:
|
||||||
|
type: string
|
||||||
|
onvif:
|
||||||
|
type: boolean
|
||||||
|
onvif_password:
|
||||||
|
type: string
|
||||||
|
onvif_username:
|
||||||
|
type: string
|
||||||
|
onvif_xaddr:
|
||||||
|
type: string
|
||||||
|
rtsp:
|
||||||
|
type: string
|
||||||
|
sub_rtsp:
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
|
models.KStorage:
|
||||||
|
properties:
|
||||||
|
access_key:
|
||||||
|
type: string
|
||||||
|
cloud_key:
|
||||||
|
type: string
|
||||||
|
directory:
|
||||||
|
type: string
|
||||||
|
provider:
|
||||||
|
type: string
|
||||||
|
secret_access_key:
|
||||||
|
type: string
|
||||||
|
uri:
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
|
models.Polygon:
|
||||||
|
properties:
|
||||||
|
coordinates:
|
||||||
|
items:
|
||||||
|
$ref: '#/definitions/models.Coordinate'
|
||||||
|
type: array
|
||||||
|
id:
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
|
models.RaspiCamera:
|
||||||
|
properties:
|
||||||
|
device:
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
|
models.Rectangle:
|
||||||
|
properties:
|
||||||
|
x1:
|
||||||
|
type: integer
|
||||||
|
x2:
|
||||||
|
type: integer
|
||||||
|
y1:
|
||||||
|
type: integer
|
||||||
|
y2:
|
||||||
|
type: integer
|
||||||
|
type: object
|
||||||
|
models.Region:
|
||||||
|
properties:
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
polygon:
|
||||||
|
items:
|
||||||
|
$ref: '#/definitions/models.Polygon'
|
||||||
|
type: array
|
||||||
|
rectangle:
|
||||||
|
$ref: '#/definitions/models.Rectangle'
|
||||||
|
type: object
|
||||||
|
models.S3:
|
||||||
|
properties:
|
||||||
|
bucket:
|
||||||
|
type: string
|
||||||
|
proxy:
|
||||||
|
type: string
|
||||||
|
proxyuri:
|
||||||
|
type: string
|
||||||
|
publickey:
|
||||||
|
type: string
|
||||||
|
region:
|
||||||
|
type: string
|
||||||
|
secretkey:
|
||||||
|
type: string
|
||||||
|
username:
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
|
models.Timetable:
|
||||||
|
properties:
|
||||||
|
end1:
|
||||||
|
type: integer
|
||||||
|
end2:
|
||||||
|
type: integer
|
||||||
|
start1:
|
||||||
|
type: integer
|
||||||
|
start2:
|
||||||
|
type: integer
|
||||||
|
type: object
|
||||||
|
models.USBCamera:
|
||||||
|
properties:
|
||||||
|
device:
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
info:
|
info:
|
||||||
contact:
|
contact:
|
||||||
email: support@kerberos.io
|
email: support@kerberos.io
|
||||||
@@ -11,7 +211,49 @@ info:
|
|||||||
termsOfService: https://kerberos.io
|
termsOfService: https://kerberos.io
|
||||||
title: Swagger Kerberos Agent API
|
title: Swagger Kerberos Agent API
|
||||||
version: "1.0"
|
version: "1.0"
|
||||||
paths: {}
|
paths:
|
||||||
|
/api/hub/verify:
|
||||||
|
post:
|
||||||
|
description: Will verify the hub connectivity.
|
||||||
|
operationId: verify-hub
|
||||||
|
parameters:
|
||||||
|
- description: Config
|
||||||
|
in: body
|
||||||
|
name: config
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/models.Config'
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/models.APIResponse'
|
||||||
|
security:
|
||||||
|
- Bearer: []
|
||||||
|
summary: Will verify the hub connectivity.
|
||||||
|
tags:
|
||||||
|
- config
|
||||||
|
/api/persistence/verify:
|
||||||
|
post:
|
||||||
|
description: Will verify the persistence.
|
||||||
|
operationId: verify-persistence
|
||||||
|
parameters:
|
||||||
|
- description: Config
|
||||||
|
in: body
|
||||||
|
name: config
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/models.Config'
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/models.APIResponse'
|
||||||
|
security:
|
||||||
|
- Bearer: []
|
||||||
|
summary: Will verify the persistence.
|
||||||
|
tags:
|
||||||
|
- config
|
||||||
securityDefinitions:
|
securityDefinitions:
|
||||||
Bearer:
|
Bearer:
|
||||||
in: header
|
in: header
|
||||||
|
11
machinery/src/models/Media.go
Normal file
11
machinery/src/models/Media.go
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
package models
|
||||||
|
|
||||||
|
type Media struct {
|
||||||
|
Key string `json:"key"`
|
||||||
|
Path string `json:"path"`
|
||||||
|
Day string `json:"day"`
|
||||||
|
Time string `json:"time"`
|
||||||
|
Timestamp string `json:"timestamp"`
|
||||||
|
CameraName string `json:"camera_name"`
|
||||||
|
CameraKey string `json:"camera_key"`
|
||||||
|
}
|
@@ -71,17 +71,66 @@ func AddRoutes(r *gin.Engine, authMiddleware *jwt.GinJWTMiddleware, configuratio
|
|||||||
cloudTimestamp = communication.CloudTimestamp.Load().(int64)
|
cloudTimestamp = communication.CloudTimestamp.Load().(int64)
|
||||||
}
|
}
|
||||||
|
|
||||||
// The total number of recordings stored in the directory
|
// The total number of recordings stored in the directory.
|
||||||
numberOfRecordings := utils.NumberOfFilesInDirectory("./data/recordings")
|
recordingDirectory := "./data/recordings"
|
||||||
|
numberOfRecordings := utils.NumberOfFilesInDirectory(recordingDirectory)
|
||||||
|
|
||||||
|
// All days stored in this agent.
|
||||||
|
days := []string{}
|
||||||
|
latestEvents := []models.Media{}
|
||||||
|
files, err := utils.ReadDirectory(recordingDirectory)
|
||||||
|
if err == nil {
|
||||||
|
events := utils.GetSortedDirectory(files)
|
||||||
|
|
||||||
|
// Get All days
|
||||||
|
days = utils.GetDays(events, recordingDirectory, configuration)
|
||||||
|
|
||||||
|
// Get all latest events
|
||||||
|
latestEvents = utils.GetMediaFormatted(events, recordingDirectory, configuration, 5) // will get 5 latest recordings.
|
||||||
|
}
|
||||||
|
|
||||||
c.JSON(200, gin.H{
|
c.JSON(200, gin.H{
|
||||||
"offlineMode": configuration.Config.Offline,
|
"offlineMode": configuration.Config.Offline,
|
||||||
"cameraOnline": lastPacketReceived,
|
"cameraOnline": lastPacketReceived,
|
||||||
"cloudOnline": cloudTimestamp,
|
"cloudOnline": cloudTimestamp,
|
||||||
"numberOfRecordings": numberOfRecordings,
|
"numberOfRecordings": numberOfRecordings,
|
||||||
|
"days": days,
|
||||||
|
"latestEvents": latestEvents,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
api.GET("/latest-events", func(c *gin.Context) {
|
||||||
|
recordingDirectory := "./data/recordings"
|
||||||
|
files, err := utils.ReadDirectory(recordingDirectory)
|
||||||
|
if err == nil {
|
||||||
|
events := utils.GetSortedDirectory(files)
|
||||||
|
fileObjects := utils.GetMediaFormatted(events, recordingDirectory, configuration, 0) // will get all recordings from the directory.
|
||||||
|
c.JSON(200, gin.H{
|
||||||
|
"events": fileObjects,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
c.JSON(400, gin.H{
|
||||||
|
"data": "Something went wrong: " + err.Error(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
api.GET("/days", func(c *gin.Context) {
|
||||||
|
recordingDirectory := "./data/recordings"
|
||||||
|
files, err := utils.ReadDirectory(recordingDirectory)
|
||||||
|
if err == nil {
|
||||||
|
events := utils.GetSortedDirectory(files)
|
||||||
|
days := utils.GetDays(events, recordingDirectory, configuration)
|
||||||
|
c.JSON(200, gin.H{
|
||||||
|
"events": days,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
c.JSON(400, gin.H{
|
||||||
|
"data": "Something went wrong: " + err.Error(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
// Streaming handler
|
// Streaming handler
|
||||||
api.GET("/stream", func(c *gin.Context) {
|
api.GET("/stream", func(c *gin.Context) {
|
||||||
// TODO add a token validation!
|
// TODO add a token validation!
|
||||||
|
@@ -63,6 +63,7 @@ func StartServer(configuration *models.Configuration, communication *models.Comm
|
|||||||
r.Use(static.Serve("/media", static.LocalFile("./www", true)))
|
r.Use(static.Serve("/media", static.LocalFile("./www", true)))
|
||||||
r.Use(static.Serve("/settings", static.LocalFile("./www", true)))
|
r.Use(static.Serve("/settings", static.LocalFile("./www", true)))
|
||||||
r.Use(static.Serve("/login", static.LocalFile("./www", true)))
|
r.Use(static.Serve("/login", static.LocalFile("./www", true)))
|
||||||
|
r.StaticFS("/file", gin.Dir("./data/recordings", false))
|
||||||
|
|
||||||
// Run the api on port
|
// Run the api on port
|
||||||
err = r.Run(":" + configuration.Port)
|
err = r.Run(":" + configuration.Port)
|
||||||
|
@@ -7,9 +7,13 @@ import (
|
|||||||
"math/rand"
|
"math/rand"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/kerberos-io/agent/machinery/src/log"
|
"github.com/kerberos-io/agent/machinery/src/log"
|
||||||
|
"github.com/kerberos-io/agent/machinery/src/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
const letterBytes = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
const letterBytes = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||||
@@ -55,6 +59,80 @@ func ReadDirectory(directory string) ([]os.FileInfo, error) {
|
|||||||
return ff, err
|
return ff, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetSortedDirectory(files []os.FileInfo) []os.FileInfo {
|
||||||
|
sort.Slice(files, func(i, j int) bool {
|
||||||
|
return files[i].ModTime().After(files[j].ModTime())
|
||||||
|
})
|
||||||
|
return files
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetMediaFormatted(files []os.FileInfo, recordingDirectory string, configuration *models.Configuration, numberOfMedia int) []models.Media {
|
||||||
|
filePaths := []models.Media{}
|
||||||
|
count := 0
|
||||||
|
for _, file := range files {
|
||||||
|
fileName := file.Name()
|
||||||
|
fileParts := strings.Split(fileName, "_")
|
||||||
|
if len(fileParts) == 6 {
|
||||||
|
timestamp := fileParts[0]
|
||||||
|
timestampInt, err := strconv.ParseInt(timestamp, 10, 64)
|
||||||
|
if err == nil {
|
||||||
|
loc, _ := time.LoadLocation(configuration.Config.Timezone)
|
||||||
|
time := time.Unix(timestampInt, 0).In(loc)
|
||||||
|
day := time.Format("02-01-2006")
|
||||||
|
timeString := time.Format("15:04:05")
|
||||||
|
|
||||||
|
media := models.Media{
|
||||||
|
Key: fileName,
|
||||||
|
Path: recordingDirectory + "/" + fileName,
|
||||||
|
CameraName: configuration.Config.Name,
|
||||||
|
CameraKey: configuration.Config.Key,
|
||||||
|
Day: day,
|
||||||
|
Time: timeString,
|
||||||
|
Timestamp: timestamp,
|
||||||
|
}
|
||||||
|
filePaths = append(filePaths, media)
|
||||||
|
count = count + 1
|
||||||
|
if numberOfMedia > 0 && count > numberOfMedia {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return filePaths
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetDays(files []os.FileInfo, recordingDirectory string, configuration *models.Configuration) []string {
|
||||||
|
days := []string{}
|
||||||
|
for _, file := range files {
|
||||||
|
fileName := file.Name()
|
||||||
|
fileParts := strings.Split(fileName, "_")
|
||||||
|
if len(fileParts) == 6 {
|
||||||
|
timestamp := fileParts[0]
|
||||||
|
timestampInt, err := strconv.ParseInt(timestamp, 10, 64)
|
||||||
|
if err == nil {
|
||||||
|
loc, _ := time.LoadLocation(configuration.Config.Timezone)
|
||||||
|
time := time.Unix(timestampInt, 0).In(loc)
|
||||||
|
day := time.Format("02-01-2006")
|
||||||
|
days = append(days, day)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
uniqueDays := Unique(days)
|
||||||
|
return uniqueDays
|
||||||
|
}
|
||||||
|
|
||||||
|
func Unique(intSlice []string) []string {
|
||||||
|
keys := make(map[string]bool)
|
||||||
|
list := []string{}
|
||||||
|
for _, entry := range intSlice {
|
||||||
|
if _, value := keys[entry]; !value {
|
||||||
|
keys[entry] = true
|
||||||
|
list = append(list, entry)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return list
|
||||||
|
}
|
||||||
|
|
||||||
func NumberOfFilesInDirectory(path string) int {
|
func NumberOfFilesInDirectory(path string) int {
|
||||||
files, _ := ioutil.ReadDir(path)
|
files, _ := ioutil.ReadDir(path)
|
||||||
return len(files)
|
return len(files)
|
||||||
|
@@ -20,6 +20,9 @@
|
|||||||
"class-methods-use-this": "off",
|
"class-methods-use-this": "off",
|
||||||
"react/forbid-prop-types": "off",
|
"react/forbid-prop-types": "off",
|
||||||
"no-case-declarations": "off",
|
"no-case-declarations": "off",
|
||||||
|
"jsx-a11y/media-has-caption": "off",
|
||||||
|
"jsx-a11y/click-events-have-key-events": "off",
|
||||||
|
"jsx-a11y/no-noninteractive-element-interactions": "off",
|
||||||
"jsx-a11y/label-has-associated-control": [
|
"jsx-a11y/label-has-associated-control": [
|
||||||
"error",
|
"error",
|
||||||
{
|
{
|
||||||
|
@@ -42,12 +42,7 @@ class App extends React.Component {
|
|||||||
const { children, username, dashboard, dispatchLogout } = this.props;
|
const { children, username, dashboard, dispatchLogout } = this.props;
|
||||||
return (
|
return (
|
||||||
<div id="page-root">
|
<div id="page-root">
|
||||||
<Sidebar
|
<Sidebar logo={logo} title="Kerberos Agent" version="beta 1.0" mobile>
|
||||||
logo={logo}
|
|
||||||
title="Kerberos Agent"
|
|
||||||
version={config.VERSION}
|
|
||||||
mobile
|
|
||||||
>
|
|
||||||
<Profilebar
|
<Profilebar
|
||||||
username={username}
|
username={username}
|
||||||
email="support@kerberos.io"
|
email="support@kerberos.io"
|
||||||
@@ -75,7 +70,7 @@ class App extends React.Component {
|
|||||||
title="Swagger API docs"
|
title="Swagger API docs"
|
||||||
icon="api"
|
icon="api"
|
||||||
external
|
external
|
||||||
link={`${config.API_URL}swagger/index.html`}
|
link={`${config.URL}/swagger/index.html`}
|
||||||
/>
|
/>
|
||||||
<NavigationItem
|
<NavigationItem
|
||||||
title="Documentation"
|
title="Documentation"
|
||||||
@@ -98,8 +93,10 @@ class App extends React.Component {
|
|||||||
{dashboard.offlineMode === 'true' && (
|
{dashboard.offlineMode === 'true' && (
|
||||||
<Link to="/settings">
|
<Link to="/settings">
|
||||||
<div className="warning">
|
<div className="warning">
|
||||||
<Icon label="info" />
|
<div>
|
||||||
Attention! Kerberos is currently running in Offline mode.
|
<Icon label="info" />
|
||||||
|
Attention! Kerberos is currently running in Offline mode.
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Link>
|
</Link>
|
||||||
)}
|
)}
|
||||||
|
@@ -5,20 +5,15 @@
|
|||||||
background: var(--upper-gradient);
|
background: var(--upper-gradient);
|
||||||
width: 100%;
|
width: 100%;
|
||||||
color: white;
|
color: white;
|
||||||
padding: size(1.5) size(4);
|
|
||||||
margin: -6px 0 0 0;
|
margin: -6px 0 0 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
|
> div {
|
||||||
|
padding: size(1.5) size(4);
|
||||||
|
}
|
||||||
|
|
||||||
svg {
|
svg {
|
||||||
padding-right: size(1);
|
padding-right: size(1);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
a {
|
|
||||||
display: flex;
|
|
||||||
color: var(--text);
|
|
||||||
|
|
||||||
&:hover, &:active {
|
|
||||||
color: var(--text);
|
|
||||||
}
|
|
||||||
}
|
}
|
@@ -12,8 +12,13 @@ import {
|
|||||||
TableRow,
|
TableRow,
|
||||||
Icon,
|
Icon,
|
||||||
Ellipse,
|
Ellipse,
|
||||||
|
Button,
|
||||||
Card,
|
Card,
|
||||||
SetupBox,
|
SetupBox,
|
||||||
|
Modal,
|
||||||
|
ModalHeader,
|
||||||
|
ModalBody,
|
||||||
|
ModalFooter,
|
||||||
} from '@kerberos-io/ui';
|
} from '@kerberos-io/ui';
|
||||||
import './Dashboard.scss';
|
import './Dashboard.scss';
|
||||||
import ReactTooltip from 'react-tooltip';
|
import ReactTooltip from 'react-tooltip';
|
||||||
@@ -25,6 +30,8 @@ class Dashboard extends React.Component {
|
|||||||
super();
|
super();
|
||||||
this.state = {
|
this.state = {
|
||||||
liveviewLoaded: false,
|
liveviewLoaded: false,
|
||||||
|
open: false,
|
||||||
|
currentRecording: '',
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -46,13 +53,27 @@ class Dashboard extends React.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleClose() {
|
||||||
|
this.setState({
|
||||||
|
open: false,
|
||||||
|
currentRecording: '',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
getCurrentTimestamp() {
|
getCurrentTimestamp() {
|
||||||
return Math.round(Date.now() / 1000);
|
return Math.round(Date.now() / 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
openModal(file) {
|
||||||
|
this.setState({
|
||||||
|
open: true,
|
||||||
|
currentRecording: file,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { dashboard } = this.props;
|
const { dashboard } = this.props;
|
||||||
const { liveviewLoaded } = this.state;
|
const { liveviewLoaded, open, currentRecording } = this.state;
|
||||||
|
|
||||||
// We check if the camera was getting a valid frame
|
// We check if the camera was getting a valid frame
|
||||||
// during the last 5 seconds, otherwise we assume the camera is offline.
|
// during the last 5 seconds, otherwise we assume the camera is offline.
|
||||||
@@ -70,23 +91,30 @@ class Dashboard extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div id="dashboard">
|
||||||
<Breadcrumb
|
<Breadcrumb
|
||||||
title="Dashboard"
|
title="Dashboard"
|
||||||
level1="Overview of your video surveilance"
|
level1="Overview of your video surveilance"
|
||||||
level1Link=""
|
level1Link=""
|
||||||
>
|
>
|
||||||
{/* <Link to="/deployments">
|
<Link to="/media">
|
||||||
|
<Button label="Watch recordings" icon="media" type="default" />
|
||||||
|
</Link>
|
||||||
|
<Link to="/settings">
|
||||||
<Button
|
<Button
|
||||||
label="Add Kerberos Agent"
|
label="Configure"
|
||||||
icon="plus-circle"
|
icon="preferences"
|
||||||
type="default"
|
type={isCameraOnline ? 'neutral' : 'default'}
|
||||||
/>
|
/>
|
||||||
</Link> */}
|
</Link>
|
||||||
</Breadcrumb>
|
</Breadcrumb>
|
||||||
|
|
||||||
<div className="stats grid-container --four-columns">
|
<div className="stats grid-container --four-columns">
|
||||||
<KPI number="69" divider="0" footer="Number of days" />
|
<KPI
|
||||||
|
number={dashboard.days ? dashboard.days.length : 0}
|
||||||
|
divider="0"
|
||||||
|
footer="Number of days"
|
||||||
|
/>
|
||||||
<KPI
|
<KPI
|
||||||
number={
|
number={
|
||||||
dashboard.numberOfRecordings ? dashboard.numberOfRecordings : 0
|
dashboard.numberOfRecordings ? dashboard.numberOfRecordings : 0
|
||||||
@@ -128,87 +156,76 @@ class Dashboard extends React.Component {
|
|||||||
/>
|
/>
|
||||||
</TableHeader>
|
</TableHeader>
|
||||||
<TableBody>
|
<TableBody>
|
||||||
<TableRow
|
{dashboard.latestEvents &&
|
||||||
id="cells1"
|
dashboard.latestEvents.map((event) => (
|
||||||
bodycells={[
|
<TableRow
|
||||||
<>
|
key={event.timestamp}
|
||||||
<Ellipse status="success" />{' '}
|
id="cells1"
|
||||||
<p data-tip="10m and 5s ago">19:45:10</p>
|
bodycells={[
|
||||||
</>,
|
<>
|
||||||
<>
|
<div className="time">
|
||||||
<p>Motion was detected</p>
|
<Ellipse status="success" />{' '}
|
||||||
</>,
|
<p data-tip="10m and 5s ago">{event.time}</p>
|
||||||
<>
|
</div>
|
||||||
<span className="version">Frontdoor</span>
|
</>,
|
||||||
<Icon label="cameras" />
|
<>
|
||||||
</>,
|
<p
|
||||||
]}
|
className="pointer"
|
||||||
/>
|
onClick={() =>
|
||||||
<TableRow
|
this.openModal(`${config.URL}/file/${event.key}`)
|
||||||
id="cells1"
|
}
|
||||||
bodycells={[
|
>
|
||||||
<>
|
Motion was detected
|
||||||
<Ellipse status="success" />{' '}
|
</p>
|
||||||
<p data-tip="10m and 5s ago">18:23:44</p>
|
</>,
|
||||||
</>,
|
<>
|
||||||
<>
|
<span className="version">{event.camera_name}</span>
|
||||||
<p>Motion was detected</p>
|
|
||||||
</>,
|
<Icon label="cameras" />
|
||||||
<>
|
</>,
|
||||||
<span>Frontdoor</span>
|
]}
|
||||||
<Icon label="cameras" />
|
/>
|
||||||
</>,
|
))}
|
||||||
]}
|
|
||||||
/>
|
|
||||||
<TableRow
|
|
||||||
id="cells1"
|
|
||||||
bodycells={[
|
|
||||||
<>
|
|
||||||
<Ellipse status="success" />{' '}
|
|
||||||
<p data-tip="10m and 5s ago">18:20:29</p>
|
|
||||||
</>,
|
|
||||||
<>
|
|
||||||
<p>Motion was detected</p>
|
|
||||||
</>,
|
|
||||||
<>
|
|
||||||
<span className="version">Frontdoor</span>
|
|
||||||
<Icon label="cameras" />
|
|
||||||
</>,
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
<TableRow
|
|
||||||
id="cells1"
|
|
||||||
bodycells={[
|
|
||||||
<>
|
|
||||||
<Ellipse status="success" />{' '}
|
|
||||||
<p data-tip="10m and 5s ago">15:16:58</p>
|
|
||||||
</>,
|
|
||||||
<>
|
|
||||||
<p>Motion was detected</p>
|
|
||||||
</>,
|
|
||||||
<>
|
|
||||||
<span className="version">Frontdoor</span>
|
|
||||||
<Icon label="cameras" />
|
|
||||||
</>,
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
<TableRow
|
|
||||||
id="cells1"
|
|
||||||
bodycells={[
|
|
||||||
<>
|
|
||||||
<Ellipse status="success" />{' '}
|
|
||||||
<p data-tip="10m and 5s ago">10:05:44</p>
|
|
||||||
</>,
|
|
||||||
<>
|
|
||||||
<p>Motion was detected</p>
|
|
||||||
</>,
|
|
||||||
<>
|
|
||||||
<span className="version">Frontdoor</span>
|
|
||||||
<Icon label="cameras" />
|
|
||||||
</>,
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
</TableBody>
|
</TableBody>
|
||||||
|
{open && (
|
||||||
|
<Modal>
|
||||||
|
<ModalHeader
|
||||||
|
title="View recording"
|
||||||
|
onClose={() => this.handleClose()}
|
||||||
|
/>
|
||||||
|
<ModalBody>
|
||||||
|
<video controls autoPlay>
|
||||||
|
<source src={currentRecording} type="video/mp4" />
|
||||||
|
</video>
|
||||||
|
</ModalBody>
|
||||||
|
<ModalFooter
|
||||||
|
right={
|
||||||
|
<>
|
||||||
|
<a
|
||||||
|
href={currentRecording}
|
||||||
|
download="video"
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
label="Download"
|
||||||
|
icon="download"
|
||||||
|
type="button"
|
||||||
|
buttonType="button"
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
<Button
|
||||||
|
label="Close"
|
||||||
|
icon="cross-circle"
|
||||||
|
type="button"
|
||||||
|
buttonType="button"
|
||||||
|
onClick={() => this.handleClose()}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Modal>
|
||||||
|
)}
|
||||||
</Table>
|
</Table>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
|
@@ -1,7 +1,31 @@
|
|||||||
hr {
|
#dashboard {
|
||||||
margin-top: 50px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dashed {
|
hr {
|
||||||
padding: 20%;
|
margin-top: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dashed {
|
||||||
|
padding: 20%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pointer {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
display: flex;
|
||||||
|
color: var(--text);
|
||||||
|
|
||||||
|
&:hover, &:active {
|
||||||
|
color: var(--text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.time {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
.ellipse-container {
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
@@ -4,8 +4,10 @@ import {
|
|||||||
VideoContainer,
|
VideoContainer,
|
||||||
VideoCard,
|
VideoCard,
|
||||||
ControlBar,
|
ControlBar,
|
||||||
|
Button,
|
||||||
Input,
|
Input,
|
||||||
} from '@kerberos-io/ui';
|
} from '@kerberos-io/ui';
|
||||||
|
import { Link } from 'react-router-dom';
|
||||||
import styles from './Media.scss';
|
import styles from './Media.scss';
|
||||||
|
|
||||||
// eslint-disable-next-line react/prefer-stateless-function
|
// eslint-disable-next-line react/prefer-stateless-function
|
||||||
@@ -18,13 +20,9 @@ class Media extends React.Component {
|
|||||||
level1="All your recordings in a single place"
|
level1="All your recordings in a single place"
|
||||||
level1Link=""
|
level1Link=""
|
||||||
>
|
>
|
||||||
{/* <Link to="/deployments">
|
<Link to="/settings">
|
||||||
<Button
|
<Button label="Configure" icon="preferences" type="default" />
|
||||||
label="Add Kerberos Agent"
|
</Link>
|
||||||
icon="plus-circle"
|
|
||||||
type="default"
|
|
||||||
/>
|
|
||||||
</Link> */}
|
|
||||||
</Breadcrumb>
|
</Breadcrumb>
|
||||||
|
|
||||||
<ControlBar>
|
<ControlBar>
|
||||||
|
@@ -16,7 +16,7 @@ import {
|
|||||||
Icon,
|
Icon,
|
||||||
Toggle,
|
Toggle,
|
||||||
} from '@kerberos-io/ui';
|
} from '@kerberos-io/ui';
|
||||||
import { withRouter } from 'react-router-dom';
|
import { Link, withRouter } from 'react-router-dom';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import ImageCanvas from '../../components/ImageCanvas/ImageCanvas';
|
import ImageCanvas from '../../components/ImageCanvas/ImageCanvas';
|
||||||
import './Settings.scss';
|
import './Settings.scss';
|
||||||
@@ -467,11 +467,11 @@ class Settings extends React.Component {
|
|||||||
|
|
||||||
return config ? (
|
return config ? (
|
||||||
<div id="settings">
|
<div id="settings">
|
||||||
<Breadcrumb
|
<Breadcrumb title="Settings" level1="Onboard your camera" level1Link="">
|
||||||
title="Settings"
|
<Link to="/media">
|
||||||
level1="Onboard your camera"
|
<Button label="Watch recordings" icon="media" type="default" />
|
||||||
level1Link=""
|
</Link>
|
||||||
/>
|
</Breadcrumb>
|
||||||
<ControlBar type="row">
|
<ControlBar type="row">
|
||||||
<Tabs>
|
<Tabs>
|
||||||
<Tab
|
<Tab
|
||||||
|
Reference in New Issue
Block a user