mirror of
https://github.com/kerberos-io/agent.git
synced 2025-10-23 00:09:40 +08:00
update dashboard page, add breadcrumb buttons
This commit is contained in:
@@ -24,7 +24,382 @@ const docTemplate = `{
|
||||
},
|
||||
"host": "{{.Host}}",
|
||||
"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": {
|
||||
"Bearer": {
|
||||
"type": "apiKey",
|
||||
|
@@ -16,7 +16,382 @@
|
||||
"version": "1.0"
|
||||
},
|
||||
"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": {
|
||||
"Bearer": {
|
||||
"type": "apiKey",
|
||||
|
@@ -1,4 +1,204 @@
|
||||
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:
|
||||
contact:
|
||||
email: support@kerberos.io
|
||||
@@ -11,7 +211,49 @@ info:
|
||||
termsOfService: https://kerberos.io
|
||||
title: Swagger Kerberos Agent API
|
||||
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:
|
||||
Bearer:
|
||||
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)
|
||||
}
|
||||
|
||||
// The total number of recordings stored in the directory
|
||||
numberOfRecordings := utils.NumberOfFilesInDirectory("./data/recordings")
|
||||
// The total number of recordings stored in the directory.
|
||||
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{
|
||||
"offlineMode": configuration.Config.Offline,
|
||||
"cameraOnline": lastPacketReceived,
|
||||
"cloudOnline": cloudTimestamp,
|
||||
"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
|
||||
api.GET("/stream", func(c *gin.Context) {
|
||||
// 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("/settings", 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
|
||||
err = r.Run(":" + configuration.Port)
|
||||
|
@@ -7,9 +7,13 @@ import (
|
||||
"math/rand"
|
||||
"os"
|
||||
"os/exec"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/kerberos-io/agent/machinery/src/log"
|
||||
"github.com/kerberos-io/agent/machinery/src/models"
|
||||
)
|
||||
|
||||
const letterBytes = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
@@ -55,6 +59,80 @@ func ReadDirectory(directory string) ([]os.FileInfo, error) {
|
||||
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 {
|
||||
files, _ := ioutil.ReadDir(path)
|
||||
return len(files)
|
||||
|
@@ -20,6 +20,9 @@
|
||||
"class-methods-use-this": "off",
|
||||
"react/forbid-prop-types": "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": [
|
||||
"error",
|
||||
{
|
||||
|
@@ -42,12 +42,7 @@ class App extends React.Component {
|
||||
const { children, username, dashboard, dispatchLogout } = this.props;
|
||||
return (
|
||||
<div id="page-root">
|
||||
<Sidebar
|
||||
logo={logo}
|
||||
title="Kerberos Agent"
|
||||
version={config.VERSION}
|
||||
mobile
|
||||
>
|
||||
<Sidebar logo={logo} title="Kerberos Agent" version="beta 1.0" mobile>
|
||||
<Profilebar
|
||||
username={username}
|
||||
email="support@kerberos.io"
|
||||
@@ -75,7 +70,7 @@ class App extends React.Component {
|
||||
title="Swagger API docs"
|
||||
icon="api"
|
||||
external
|
||||
link={`${config.API_URL}swagger/index.html`}
|
||||
link={`${config.URL}/swagger/index.html`}
|
||||
/>
|
||||
<NavigationItem
|
||||
title="Documentation"
|
||||
@@ -98,9 +93,11 @@ class App extends React.Component {
|
||||
{dashboard.offlineMode === 'true' && (
|
||||
<Link to="/settings">
|
||||
<div className="warning">
|
||||
<div>
|
||||
<Icon label="info" />
|
||||
Attention! Kerberos is currently running in Offline mode.
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
)}
|
||||
|
||||
|
@@ -5,20 +5,15 @@
|
||||
background: var(--upper-gradient);
|
||||
width: 100%;
|
||||
color: white;
|
||||
padding: size(1.5) size(4);
|
||||
margin: -6px 0 0 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
> div {
|
||||
padding: size(1.5) size(4);
|
||||
}
|
||||
|
||||
svg {
|
||||
padding-right: size(1);
|
||||
}
|
||||
}
|
||||
a {
|
||||
display: flex;
|
||||
color: var(--text);
|
||||
|
||||
&:hover, &:active {
|
||||
color: var(--text);
|
||||
}
|
||||
}
|
@@ -12,8 +12,13 @@ import {
|
||||
TableRow,
|
||||
Icon,
|
||||
Ellipse,
|
||||
Button,
|
||||
Card,
|
||||
SetupBox,
|
||||
Modal,
|
||||
ModalHeader,
|
||||
ModalBody,
|
||||
ModalFooter,
|
||||
} from '@kerberos-io/ui';
|
||||
import './Dashboard.scss';
|
||||
import ReactTooltip from 'react-tooltip';
|
||||
@@ -25,6 +30,8 @@ class Dashboard extends React.Component {
|
||||
super();
|
||||
this.state = {
|
||||
liveviewLoaded: false,
|
||||
open: false,
|
||||
currentRecording: '',
|
||||
};
|
||||
}
|
||||
|
||||
@@ -46,13 +53,27 @@ class Dashboard extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
handleClose() {
|
||||
this.setState({
|
||||
open: false,
|
||||
currentRecording: '',
|
||||
});
|
||||
}
|
||||
|
||||
getCurrentTimestamp() {
|
||||
return Math.round(Date.now() / 1000);
|
||||
}
|
||||
|
||||
openModal(file) {
|
||||
this.setState({
|
||||
open: true,
|
||||
currentRecording: file,
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
const { dashboard } = this.props;
|
||||
const { liveviewLoaded } = this.state;
|
||||
const { liveviewLoaded, open, currentRecording } = this.state;
|
||||
|
||||
// We check if the camera was getting a valid frame
|
||||
// during the last 5 seconds, otherwise we assume the camera is offline.
|
||||
@@ -70,23 +91,30 @@ class Dashboard extends React.Component {
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div id="dashboard">
|
||||
<Breadcrumb
|
||||
title="Dashboard"
|
||||
level1="Overview of your video surveilance"
|
||||
level1Link=""
|
||||
>
|
||||
{/* <Link to="/deployments">
|
||||
<Link to="/media">
|
||||
<Button label="Watch recordings" icon="media" type="default" />
|
||||
</Link>
|
||||
<Link to="/settings">
|
||||
<Button
|
||||
label="Add Kerberos Agent"
|
||||
icon="plus-circle"
|
||||
type="default"
|
||||
label="Configure"
|
||||
icon="preferences"
|
||||
type={isCameraOnline ? 'neutral' : 'default'}
|
||||
/>
|
||||
</Link> */}
|
||||
</Link>
|
||||
</Breadcrumb>
|
||||
|
||||
<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
|
||||
number={
|
||||
dashboard.numberOfRecordings ? dashboard.numberOfRecordings : 0
|
||||
@@ -128,87 +156,76 @@ class Dashboard extends React.Component {
|
||||
/>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{dashboard.latestEvents &&
|
||||
dashboard.latestEvents.map((event) => (
|
||||
<TableRow
|
||||
key={event.timestamp}
|
||||
id="cells1"
|
||||
bodycells={[
|
||||
<>
|
||||
<div className="time">
|
||||
<Ellipse status="success" />{' '}
|
||||
<p data-tip="10m and 5s ago">19:45:10</p>
|
||||
<p data-tip="10m and 5s ago">{event.time}</p>
|
||||
</div>
|
||||
</>,
|
||||
<>
|
||||
<p>Motion was detected</p>
|
||||
<p
|
||||
className="pointer"
|
||||
onClick={() =>
|
||||
this.openModal(`${config.URL}/file/${event.key}`)
|
||||
}
|
||||
>
|
||||
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">18:23:44</p>
|
||||
</>,
|
||||
<>
|
||||
<p>Motion was detected</p>
|
||||
</>,
|
||||
<>
|
||||
<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>
|
||||
<span className="version">{event.camera_name}</span>
|
||||
|
||||
<Icon label="cameras" />
|
||||
</>,
|
||||
]}
|
||||
/>
|
||||
))}
|
||||
</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>
|
||||
</div>
|
||||
<div>
|
||||
|
@@ -1,7 +1,31 @@
|
||||
hr {
|
||||
margin-top: 50px;
|
||||
}
|
||||
#dashboard {
|
||||
|
||||
.dashed {
|
||||
hr {
|
||||
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,
|
||||
VideoCard,
|
||||
ControlBar,
|
||||
Button,
|
||||
Input,
|
||||
} from '@kerberos-io/ui';
|
||||
import { Link } from 'react-router-dom';
|
||||
import styles from './Media.scss';
|
||||
|
||||
// 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"
|
||||
level1Link=""
|
||||
>
|
||||
{/* <Link to="/deployments">
|
||||
<Button
|
||||
label="Add Kerberos Agent"
|
||||
icon="plus-circle"
|
||||
type="default"
|
||||
/>
|
||||
</Link> */}
|
||||
<Link to="/settings">
|
||||
<Button label="Configure" icon="preferences" type="default" />
|
||||
</Link>
|
||||
</Breadcrumb>
|
||||
|
||||
<ControlBar>
|
||||
|
@@ -16,7 +16,7 @@ import {
|
||||
Icon,
|
||||
Toggle,
|
||||
} from '@kerberos-io/ui';
|
||||
import { withRouter } from 'react-router-dom';
|
||||
import { Link, withRouter } from 'react-router-dom';
|
||||
import { connect } from 'react-redux';
|
||||
import ImageCanvas from '../../components/ImageCanvas/ImageCanvas';
|
||||
import './Settings.scss';
|
||||
@@ -467,11 +467,11 @@ class Settings extends React.Component {
|
||||
|
||||
return config ? (
|
||||
<div id="settings">
|
||||
<Breadcrumb
|
||||
title="Settings"
|
||||
level1="Onboard your camera"
|
||||
level1Link=""
|
||||
/>
|
||||
<Breadcrumb title="Settings" level1="Onboard your camera" level1Link="">
|
||||
<Link to="/media">
|
||||
<Button label="Watch recordings" icon="media" type="default" />
|
||||
</Link>
|
||||
</Breadcrumb>
|
||||
<ControlBar type="row">
|
||||
<Tabs>
|
||||
<Tab
|
||||
|
Reference in New Issue
Block a user