mirror of
https://github.com/kerberos-io/agent.git
synced 2026-03-03 15:50:15 +00:00
Compare commits
36 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f7ced6056d | ||
|
|
00917e3f88 | ||
|
|
bcfed04a07 | ||
|
|
bf97bd72f1 | ||
|
|
4b8b6bf66a | ||
|
|
4b6c25bb85 | ||
|
|
729b38999e | ||
|
|
4cbf0323f1 | ||
|
|
1f5cb8ca88 | ||
|
|
8be0a04502 | ||
|
|
bdc0039a24 | ||
|
|
756b893ecd | ||
|
|
36323b076f | ||
|
|
95f43b6444 | ||
|
|
5c23a62ac3 | ||
|
|
2b425a2ddd | ||
|
|
abeeb95204 | ||
|
|
6aed20c466 | ||
|
|
6672535544 | ||
|
|
ed397b6ecc | ||
|
|
530e4c654e | ||
|
|
913bd1ba12 | ||
|
|
84e532be47 | ||
|
|
3341e99af1 | ||
|
|
ced6e678ec | ||
|
|
340a5d7ef6 | ||
|
|
60e8edc876 | ||
|
|
9cf9babd73 | ||
|
|
229c246e1c | ||
|
|
15d9bcda4f | ||
|
|
068063695e | ||
|
|
b1722844f3 | ||
|
|
eb5ab48d6c | ||
|
|
b64f1039d7 | ||
|
|
6fcd6e53a1 | ||
|
|
25537b5f02 |
@@ -32,7 +32,7 @@ RUN cat /go/src/github.com/kerberos-io/agent/machinery/version
|
||||
|
||||
RUN cd /go/src/github.com/kerberos-io/agent/machinery && \
|
||||
go mod download && \
|
||||
go build -tags timetzdata,netgo --ldflags '-s -w -extldflags "-static -latomic"' main.go && \
|
||||
go build -tags timetzdata,netgo,osusergo --ldflags '-s -w -extldflags "-static -latomic"' main.go && \
|
||||
mkdir -p /agent && \
|
||||
mv main /agent && \
|
||||
mv version /agent && \
|
||||
@@ -122,6 +122,7 @@ RUN cp /home/agent/data/config/config.json /home/agent/data/config.template.json
|
||||
# Set permissions correctly
|
||||
|
||||
RUN chown -R agent:kerberosio /home/agent/data
|
||||
RUN chown -R agent:kerberosio /home/agent/www
|
||||
|
||||
###########################
|
||||
# Grant the necessary root capabilities to the process trying to bind to the privileged port
|
||||
@@ -146,4 +147,4 @@ HEALTHCHECK CMD curl --fail http://localhost:80 || exit 1
|
||||
# Leeeeettttt'ssss goooooo!!!
|
||||
# Run the shizzle from the right working directory.
|
||||
WORKDIR /home/agent
|
||||
CMD ["./main", "run", "opensource", "80"]
|
||||
CMD ["./main", "-action", "run", "-port", "80"]
|
||||
|
||||
22
README.md
22
README.md
@@ -78,12 +78,9 @@ If you want to connect to an USB or Raspberry Pi camera, [you'll need to run our
|
||||
|
||||
## Quickstart - Balena
|
||||
|
||||
Run Kerberos Agent with Balena super powers. Monitor your agent with seamless remote access, and an encrypted https endpoint.
|
||||
Checkout our fleet on [Balena Hub](https://hub.balena.io/fleets?0%5B0%5D%5Bn%5D=any&0%5B0%5D%5Bo%5D=full_text_search&0%5B0%5D%5Bv%5D=agent), and add your agent.
|
||||
Run Kerberos Agent with [Balena Cloud](https://www.balena.io/) super powers. Monitor your Kerberos Agent with seamless remote access, over the air updates, an encrypted public `https` endpoint and many more. Checkout our application `video-surveillance` on [Balena Hub](https://hub.balena.io/apps/2064752/video-surveillance), and create your first or fleet of Kerberos Agent(s).
|
||||
|
||||
[](https://dashboard.balena-cloud.com/deploy?repoUrl=https://github.com/kerberos-io/agent)
|
||||
|
||||
**_Work In Progress_** - Currently we only support IP and USB Cameras, we have [an approach for leveraging the Raspberry Pi camera](https://github.com/kerberos-io/camera-to-rtsp), but this isn't working as expected with Balena. If you require this, you'll need to use the traditional Docker deployment with sidecar as mentioned above.
|
||||
[](https://dashboard.balena-cloud.com/deploy?repoUrl=https://github.com/kerberos-io/balena-agent)
|
||||
|
||||
## A world of Kerberos Agents
|
||||
|
||||
@@ -122,6 +119,7 @@ We have documented the different deployment models [in the `deployments` directo
|
||||
- [Red Hat OpenShift with Ansible](https://github.com/kerberos-io/agent/tree/master/deployments#4-red-hat-ansible-and-openshift)
|
||||
- [Terraform](https://github.com/kerberos-io/agent/tree/master/deployments#5-terraform)
|
||||
- [Salt](https://github.com/kerberos-io/agent/tree/master/deployments#6-salt)
|
||||
- [Balena](https://github.com/kerberos-io/agent/tree/master/deployments#8-balena)
|
||||
|
||||
By default your Kerberos Agents will store all its configuration and recordings inside the container. To help you automate and have a more consistent data governance, you can attach volumes to configure and persist data of your Kerberos Agents, and/or configure each Kerberos Agent through environment variables.
|
||||
|
||||
@@ -165,6 +163,8 @@ Next to attaching the configuration file, it is also possible to override the co
|
||||
|
||||
| Name | Description | Default Value |
|
||||
| --------------------------------------- | ----------------------------------------------------------------------------------------------- | ------------------------------ |
|
||||
| `AGENT_MODE` | You can choose to run this in 'release' for production, and or 'demo' for showcasing. | "release" |
|
||||
| `AGENT_TLS_INSECURE` | Specify if you want to use `InsecureSkipVerify` for the internal HTTP client. | "false" |
|
||||
| `AGENT_USERNAME` | The username used to authenticate against the Kerberos Agent login page. | "root" |
|
||||
| `AGENT_PASSWORD` | The password used to authenticate against the Kerberos Agent login page. | "root" |
|
||||
| `AGENT_KEY` | A unique identifier for your Kerberos Agent, this is auto-generated but can be overriden. | "" |
|
||||
@@ -202,7 +202,7 @@ Next to attaching the configuration file, it is also possible to override the co
|
||||
| `AGENT_HUB_URI` | The Kerberos Hub API, defaults to our Kerberos Hub SAAS. | "https://api.hub.domain.com" |
|
||||
| `AGENT_HUB_KEY` | The access key linked to your account in Kerberos Hub. | "" |
|
||||
| `AGENT_HUB_PRIVATE_KEY` | The secret access key linked to your account in Kerberos Hub. | "" |
|
||||
| `AGENT_HUB_USERNAME` | Your Kerberos Hub username, which owns the above access and secret keys. | "" |
|
||||
| `AGENT_HUB_REGION` | The Kerberos Hub region, to which you want to upload. | "" |
|
||||
| `AGENT_HUB_SITE` | The site ID of a site you've created in your Kerberos Hub account. | "" |
|
||||
| `AGENT_KERBEROSVAULT_URI` | The Kerberos Vault API url. | "https://vault.domain.com/api" |
|
||||
| `AGENT_KERBEROSVAULT_ACCESS_KEY` | The access key of a Kerberos Vault account. | "" |
|
||||
@@ -233,9 +233,9 @@ On opening of the GitHub Codespace, some dependencies will be installed. Once th
|
||||
const dev = {
|
||||
ENV: 'dev',
|
||||
HOSTNAME: externalHost,
|
||||
//API_URL: `${protocol}//${hostname}:8080/api`,
|
||||
//URL: `${protocol}//${hostname}:8080`,
|
||||
//WS_URL: `${websocketprotocol}//${hostname}:8080/ws`,
|
||||
//API_URL: `${protocol}//${hostname}:80/api`,
|
||||
//URL: `${protocol}//${hostname}:80`,
|
||||
//WS_URL: `${websocketprotocol}//${hostname}:80/ws`,
|
||||
|
||||
// Uncomment, and comment the above lines, when using codespaces or other special DNS names (which you can't control)
|
||||
API_URL: `${protocol}//${externalHost}/api`,
|
||||
@@ -248,7 +248,7 @@ Go and open two terminals one for the `ui` project and one for the `machinery` p
|
||||
1. Terminal A:
|
||||
|
||||
cd machinery/
|
||||
go run main.go run camera 80
|
||||
go run main.go -action run -port 80
|
||||
|
||||
2. Terminal B:
|
||||
|
||||
@@ -289,7 +289,7 @@ You can simply run the `machinery` using following commands.
|
||||
|
||||
git clone https://github.com/kerberos-io/agent
|
||||
cd machinery
|
||||
go run main.go run mycameraname 80
|
||||
go run main.go -action run -port 80
|
||||
|
||||
This will launch the Kerberos Agent and run a webserver on port `80`. You can change the port by your own preference. We strongly support the usage of [Goland](https://www.jetbrains.com/go/) or [Visual Studio Code](https://code.visualstudio.com/), as it comes with all the debugging and linting features builtin.
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ We will discuss following deployment models.
|
||||
- [5. Kerberos Factory](#5-kerberos-factory)
|
||||
- [6. Terraform](#6-terraform)
|
||||
- [7. Salt](#7-salt)
|
||||
- [8. Balena](#8-balena)
|
||||
|
||||
## 0. Static binary
|
||||
|
||||
@@ -58,3 +59,11 @@ To be written
|
||||
## 7. Salt
|
||||
|
||||
To be written
|
||||
|
||||
## 8. Balena
|
||||
|
||||
Balena Cloud provide a seamless way of building and deploying applications at scale through the conceps of `blocks`, `apps` and `fleets`. Once you have your `app` deployed, for example our Kerberos Agent, you can benefit from features such as: remote access, over the air updates, an encrypted public `https` endpoint and many more.
|
||||
|
||||
Together with the Balena.io team we've build a Balena App, called [`video-surveillance`](https://hub.balena.io/apps/2064752/video-surveillance), which any can use to deploy a video surveillance system in a matter of minutes with all the expected management features you can think of.
|
||||
|
||||
> Learn more [about Kerberos Agent with Balena](https://github.com/kerberos-io/agent/tree/master/deployments/balena).
|
||||
|
||||
@@ -82,7 +82,7 @@
|
||||
|
||||
initContainers:
|
||||
- name: download-config
|
||||
image: kerberos/agent:1b96d01
|
||||
image: kerberos/agent:latest
|
||||
volumeMounts:
|
||||
- name: kerberos-data
|
||||
mountPath: /home/agent/data/config
|
||||
@@ -96,7 +96,7 @@
|
||||
|
||||
containers:
|
||||
- name: agent
|
||||
image: kerberos/agent:1b96d01
|
||||
image: kerberos/agent:latest
|
||||
volumeMounts:
|
||||
- name: kerberos-data
|
||||
mountPath: /home/agent/data/config
|
||||
|
||||
31
deployments/balena/README.md
Normal file
31
deployments/balena/README.md
Normal file
@@ -0,0 +1,31 @@
|
||||
# Deployment with Balena
|
||||
|
||||
Balena Cloud provide a seamless way of building and deploying applications at scale through the conceps of `blocks`, `apps` and `fleets`. Once you have your `app` deployed, for example our Kerberos Agent, you can benefit from features such as: remote access, over the air updates, an encrypted public `https` endpoint and many more.
|
||||
|
||||
We provide two mechanisms to deploy Kerberos Agent to a Balena Cloud fleet:
|
||||
|
||||
1. Use Kerberos Agent as [a block part of your application](https://github.com/kerberos-io/balena-agent-block).
|
||||
2. Use Kerberos Agent as [a stand-alone application](https://github.com/kerberos-io/balena-agent).
|
||||
|
||||
## Block
|
||||
|
||||
Within Balena you can build the concept of a block, which is the equivalent of container image or a function in a typical programming language. The idea of blocks, you can find a more thorough explanation [here](https://docs.balena.io/learn/develop/blocks/), is that you can compose and combine multiple `blocks` to level up to the concept an `app`.
|
||||
|
||||
You as a developer can choose which `blocks` you would like to use, to build the desired `application` state you prefer. For example you can use the [Kerberos Agent block](https://hub.balena.io/blocks/2064662/agent) to compose a video surveillance system as part of your existing set of blocks.
|
||||
|
||||
You can the `Kerberos Agent` block by defining following elements in your `compose` file.
|
||||
|
||||
agent:
|
||||
image: bh.cr/kerberos_io/agent
|
||||
|
||||
## App
|
||||
|
||||
Next to building individual `blocks` you as a developer can also decide to build up an application, composed of one or more `blocks` or third-party containers, and publish it as an `app` to the Balena Hub. This is exactly [what we've done..](https://hub.balena.io/apps/2064752/video-surveillance)
|
||||
|
||||
On Balena Hub we have created the []`video-surveillance` application](https://hub.balena.io/apps/2064752/video-surveillance) that utilises the [Kerberos Agent `block`](https://hub.balena.io/blocks/2064662/agent). The idea of this application is that utilises the foundation of our Kerberos Agent, but that it might include more `blocks` over time to increase and improve functionalities from other community projects.
|
||||
|
||||
To deploy the application you can simply press below `Deploy button` or you can navigate to the [Balena Hub apps page](https://hub.balena.io/apps/2064752/video-surveillance).
|
||||
|
||||
[](https://dashboard.balena-cloud.com/deploy?repoUrl=https://github.com/kerberos-io/agent)
|
||||
|
||||
You can find the source code, `balena.yaml` and `docker-compose.yaml` files in the [`balena-agent` repository](https://github.com/kerberos-io/balena-agent).
|
||||
@@ -21,7 +21,7 @@ spec:
|
||||
|
||||
initContainers:
|
||||
- name: download-config
|
||||
image: kerberos/agent:1b96d01
|
||||
image: kerberos/agent:latest
|
||||
volumeMounts:
|
||||
- name: kerberos-data
|
||||
mountPath: /home/agent/data/config
|
||||
|
||||
2
machinery/.vscode/launch.json
vendored
2
machinery/.vscode/launch.json
vendored
@@ -10,7 +10,7 @@
|
||||
"request": "launch",
|
||||
"mode": "auto",
|
||||
"program": "main.go",
|
||||
"args": ["run", "cameraname", "8080"],
|
||||
"args": ["-action run"],
|
||||
"envFile": "${workspaceFolder}/.env",
|
||||
"buildFlags": "--tags dynamic",
|
||||
},
|
||||
|
||||
@@ -95,7 +95,7 @@
|
||||
"s3": {
|
||||
"proxyuri": "http://proxy.kerberos.io",
|
||||
"bucket": "kerberosaccept",
|
||||
"region": "eu-west1"
|
||||
"region": "eu-west-1"
|
||||
},
|
||||
"kstorage": {},
|
||||
"dropbox": {},
|
||||
@@ -112,4 +112,4 @@
|
||||
"hub_private_key": "",
|
||||
"hub_site": "",
|
||||
"condition_uri": ""
|
||||
}
|
||||
}
|
||||
|
||||
BIN
machinery/data/test-480p.mp4
Normal file
BIN
machinery/data/test-480p.mp4
Normal file
Binary file not shown.
@@ -244,6 +244,40 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/onvif/verify": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"Bearer": []
|
||||
}
|
||||
],
|
||||
"description": "Will verify the ONVIF connectivity.",
|
||||
"tags": [
|
||||
"config"
|
||||
],
|
||||
"summary": "Will verify the ONVIF connectivity.",
|
||||
"operationId": "verify-onvif",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "Camera Config",
|
||||
"name": "cameraConfig",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/models.IPCamera"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/models.APIResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/persistence/verify": {
|
||||
"post": {
|
||||
"security": [
|
||||
@@ -347,9 +381,15 @@ const docTemplate = `{
|
||||
"ipcamera": {
|
||||
"$ref": "#/definitions/models.IPCamera"
|
||||
},
|
||||
"liveview": {
|
||||
"type": "string"
|
||||
},
|
||||
"maxlengthrecording": {
|
||||
"type": "integer"
|
||||
},
|
||||
"motion": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -365,6 +405,12 @@ const docTemplate = `{
|
||||
"raspicamera": {
|
||||
"$ref": "#/definitions/models.RaspiCamera"
|
||||
},
|
||||
"recording": {
|
||||
"type": "string"
|
||||
},
|
||||
"snapshots": {
|
||||
"type": "string"
|
||||
},
|
||||
"transcodingresolution": {
|
||||
"type": "integer"
|
||||
},
|
||||
@@ -391,6 +437,12 @@ const docTemplate = `{
|
||||
"condition_uri": {
|
||||
"type": "string"
|
||||
},
|
||||
"dropbox": {
|
||||
"$ref": "#/definitions/models.Dropbox"
|
||||
},
|
||||
"friendly_name": {
|
||||
"type": "string"
|
||||
},
|
||||
"heartbeaturi": {
|
||||
"description": "obsolete",
|
||||
"type": "string"
|
||||
@@ -434,6 +486,9 @@ const docTemplate = `{
|
||||
"region": {
|
||||
"$ref": "#/definitions/models.Region"
|
||||
},
|
||||
"remove_after_upload": {
|
||||
"type": "string"
|
||||
},
|
||||
"s3": {
|
||||
"$ref": "#/definitions/models.S3"
|
||||
},
|
||||
@@ -477,6 +532,17 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"models.Dropbox": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"access_token": {
|
||||
"type": "string"
|
||||
},
|
||||
"directory": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"models.IPCamera": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -484,7 +550,7 @@ const docTemplate = `{
|
||||
"type": "string"
|
||||
},
|
||||
"onvif": {
|
||||
"type": "boolean"
|
||||
"type": "string"
|
||||
},
|
||||
"onvif_password": {
|
||||
"type": "string"
|
||||
|
||||
@@ -236,6 +236,40 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/onvif/verify": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"Bearer": []
|
||||
}
|
||||
],
|
||||
"description": "Will verify the ONVIF connectivity.",
|
||||
"tags": [
|
||||
"config"
|
||||
],
|
||||
"summary": "Will verify the ONVIF connectivity.",
|
||||
"operationId": "verify-onvif",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "Camera Config",
|
||||
"name": "cameraConfig",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/models.IPCamera"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/models.APIResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/persistence/verify": {
|
||||
"post": {
|
||||
"security": [
|
||||
@@ -339,9 +373,15 @@
|
||||
"ipcamera": {
|
||||
"$ref": "#/definitions/models.IPCamera"
|
||||
},
|
||||
"liveview": {
|
||||
"type": "string"
|
||||
},
|
||||
"maxlengthrecording": {
|
||||
"type": "integer"
|
||||
},
|
||||
"motion": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -357,6 +397,12 @@
|
||||
"raspicamera": {
|
||||
"$ref": "#/definitions/models.RaspiCamera"
|
||||
},
|
||||
"recording": {
|
||||
"type": "string"
|
||||
},
|
||||
"snapshots": {
|
||||
"type": "string"
|
||||
},
|
||||
"transcodingresolution": {
|
||||
"type": "integer"
|
||||
},
|
||||
@@ -383,6 +429,12 @@
|
||||
"condition_uri": {
|
||||
"type": "string"
|
||||
},
|
||||
"dropbox": {
|
||||
"$ref": "#/definitions/models.Dropbox"
|
||||
},
|
||||
"friendly_name": {
|
||||
"type": "string"
|
||||
},
|
||||
"heartbeaturi": {
|
||||
"description": "obsolete",
|
||||
"type": "string"
|
||||
@@ -426,6 +478,9 @@
|
||||
"region": {
|
||||
"$ref": "#/definitions/models.Region"
|
||||
},
|
||||
"remove_after_upload": {
|
||||
"type": "string"
|
||||
},
|
||||
"s3": {
|
||||
"$ref": "#/definitions/models.S3"
|
||||
},
|
||||
@@ -469,6 +524,17 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"models.Dropbox": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"access_token": {
|
||||
"type": "string"
|
||||
},
|
||||
"directory": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"models.IPCamera": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -476,7 +542,7 @@
|
||||
"type": "string"
|
||||
},
|
||||
"onvif": {
|
||||
"type": "boolean"
|
||||
"type": "string"
|
||||
},
|
||||
"onvif_password": {
|
||||
"type": "string"
|
||||
|
||||
@@ -44,8 +44,12 @@ definitions:
|
||||
type: integer
|
||||
ipcamera:
|
||||
$ref: '#/definitions/models.IPCamera'
|
||||
liveview:
|
||||
type: string
|
||||
maxlengthrecording:
|
||||
type: integer
|
||||
motion:
|
||||
type: string
|
||||
name:
|
||||
type: string
|
||||
pixelChangeThreshold:
|
||||
@@ -56,6 +60,10 @@ definitions:
|
||||
type: integer
|
||||
raspicamera:
|
||||
$ref: '#/definitions/models.RaspiCamera'
|
||||
recording:
|
||||
type: string
|
||||
snapshots:
|
||||
type: string
|
||||
transcodingresolution:
|
||||
type: integer
|
||||
transcodingwebrtc:
|
||||
@@ -73,6 +81,10 @@ definitions:
|
||||
type: string
|
||||
condition_uri:
|
||||
type: string
|
||||
dropbox:
|
||||
$ref: '#/definitions/models.Dropbox'
|
||||
friendly_name:
|
||||
type: string
|
||||
heartbeaturi:
|
||||
description: obsolete
|
||||
type: string
|
||||
@@ -102,6 +114,8 @@ definitions:
|
||||
type: string
|
||||
region:
|
||||
$ref: '#/definitions/models.Region'
|
||||
remove_after_upload:
|
||||
type: string
|
||||
s3:
|
||||
$ref: '#/definitions/models.S3'
|
||||
stunuri:
|
||||
@@ -130,12 +144,19 @@ definitions:
|
||||
"y":
|
||||
type: number
|
||||
type: object
|
||||
models.Dropbox:
|
||||
properties:
|
||||
access_token:
|
||||
type: string
|
||||
directory:
|
||||
type: string
|
||||
type: object
|
||||
models.IPCamera:
|
||||
properties:
|
||||
fps:
|
||||
type: string
|
||||
onvif:
|
||||
type: boolean
|
||||
type: string
|
||||
onvif_password:
|
||||
type: string
|
||||
onvif_username:
|
||||
@@ -414,6 +435,27 @@ paths:
|
||||
summary: Get Authorization token.
|
||||
tags:
|
||||
- authentication
|
||||
/api/onvif/verify:
|
||||
post:
|
||||
description: Will verify the ONVIF connectivity.
|
||||
operationId: verify-onvif
|
||||
parameters:
|
||||
- description: Camera Config
|
||||
in: body
|
||||
name: cameraConfig
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/models.IPCamera'
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/models.APIResponse'
|
||||
security:
|
||||
- Bearer: []
|
||||
summary: Will verify the ONVIF connectivity.
|
||||
tags:
|
||||
- config
|
||||
/api/persistence/verify:
|
||||
post:
|
||||
description: Will verify the persistence.
|
||||
|
||||
@@ -2,9 +2,8 @@ module github.com/kerberos-io/agent/machinery
|
||||
|
||||
go 1.19
|
||||
|
||||
//replace github.com/kerberos-io/joy4 v1.0.54 => ../../../../github.com/kerberos-io/joy4
|
||||
|
||||
//replace github.com/kerberos-io/onvif v0.0.5 => ../../../../github.com/kerberos-io/onvif
|
||||
// replace github.com/kerberos-io/joy4 v1.0.57 => ../../../../github.com/kerberos-io/joy4
|
||||
// replace github.com/kerberos-io/onvif v0.0.5 => ../../../../github.com/kerberos-io/onvif
|
||||
|
||||
require (
|
||||
github.com/InVisionApp/conjungo v1.1.0
|
||||
@@ -25,7 +24,7 @@ require (
|
||||
github.com/golang-module/carbon/v2 v2.2.3
|
||||
github.com/gorilla/websocket v1.5.0
|
||||
github.com/kellydunn/golang-geo v0.7.0
|
||||
github.com/kerberos-io/joy4 v1.0.57
|
||||
github.com/kerberos-io/joy4 v1.0.58
|
||||
github.com/kerberos-io/onvif v0.0.5
|
||||
github.com/minio/minio-go/v6 v6.0.57
|
||||
github.com/nsmith5/mjpeg v0.0.0-20200913181537-54b8ada0e53e
|
||||
@@ -37,8 +36,8 @@ require (
|
||||
github.com/swaggo/gin-swagger v1.5.3
|
||||
github.com/swaggo/swag v1.8.9
|
||||
github.com/tevino/abool v1.2.0
|
||||
go.mongodb.org/mongo-driver v1.7.5
|
||||
gopkg.in/DataDog/dd-trace-go.v1 v1.46.0
|
||||
gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0
|
||||
)
|
||||
|
||||
@@ -71,16 +70,19 @@ require (
|
||||
github.com/go-playground/locales v0.14.0 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.0 // indirect
|
||||
github.com/go-playground/validator/v10 v10.11.1 // indirect
|
||||
github.com/go-stack/stack v1.8.0 // indirect
|
||||
github.com/goccy/go-json v0.10.0 // indirect
|
||||
github.com/gofrs/uuid v3.2.0+incompatible // indirect
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b // indirect
|
||||
github.com/golang/protobuf v1.5.3 // indirect
|
||||
github.com/golang/snappy v0.0.4 // indirect
|
||||
github.com/google/go-cmp v0.5.9 // indirect
|
||||
github.com/google/pprof v0.0.0-20210423192551-a2663126120b // indirect
|
||||
github.com/google/uuid v1.3.0 // indirect
|
||||
github.com/joeshaw/multierror v0.0.0-20140124173710-69b34d4ec901 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/klauspost/compress v1.15.0 // indirect
|
||||
github.com/klauspost/cpuid v1.2.3 // indirect
|
||||
github.com/kylelemons/go-gypsy v1.0.0 // indirect
|
||||
github.com/leodido/go-urn v1.2.1 // indirect
|
||||
@@ -117,6 +119,10 @@ require (
|
||||
github.com/spaolacci/murmur3 v1.1.0 // indirect
|
||||
github.com/tinylib/msgp v1.1.6 // indirect
|
||||
github.com/ugorji/go/codec v1.2.7 // indirect
|
||||
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
|
||||
github.com/xdg-go/scram v1.0.2 // indirect
|
||||
github.com/xdg-go/stringprep v1.0.2 // indirect
|
||||
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect
|
||||
github.com/ziutek/mymysql v1.5.4 // indirect
|
||||
go4.org/intern v0.0.0-20211027215823-ae77deb06f29 // indirect
|
||||
go4.org/unsafe/assume-no-moving-gc v0.0.0-20220617031537-928513b29760 // indirect
|
||||
|
||||
@@ -162,6 +162,8 @@ github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GO
|
||||
github.com/go-playground/validator/v10 v10.10.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos=
|
||||
github.com/go-playground/validator/v10 v10.11.1 h1:prmOlTVv+YjZjmRmNSF3VmspqJIxJWXmqUsHwfTRRkQ=
|
||||
github.com/go-playground/validator/v10 v10.11.1/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU=
|
||||
github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
||||
github.com/goccy/go-json v0.9.7/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||
github.com/goccy/go-json v0.10.0 h1:mXKd9Qw4NuzShiRlOXKews24ufknHO7gx30lsDyokKA=
|
||||
@@ -201,6 +203,8 @@ github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaS
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
|
||||
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
|
||||
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
@@ -211,6 +215,7 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
|
||||
github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
@@ -259,11 +264,14 @@ github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7
|
||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||
github.com/kellydunn/golang-geo v0.7.0 h1:A5j0/BvNgGwY6Yb6inXQxzYwlPHc6WVZR+MrarZYNNg=
|
||||
github.com/kellydunn/golang-geo v0.7.0/go.mod h1:YYlQPJ+DPEzrHx8kT3oPHC/NjyvCCXE+IuKGKdrjrcU=
|
||||
github.com/kerberos-io/joy4 v1.0.57 h1:/8epNAJv4cOzBG8pFiM9hVNXfwsgA+8/2nHQ2yOeyII=
|
||||
github.com/kerberos-io/joy4 v1.0.57/go.mod h1:nZp4AjvKvTOXRrmDyAIOw+Da+JA5OcSo/JundGfOlFU=
|
||||
github.com/kerberos-io/joy4 v1.0.58 h1:R8EECSF+bG7o2yHC6cX/lF77Z+bDVGl6OioLZ3+5MN4=
|
||||
github.com/kerberos-io/joy4 v1.0.58/go.mod h1:nZp4AjvKvTOXRrmDyAIOw+Da+JA5OcSo/JundGfOlFU=
|
||||
github.com/kerberos-io/onvif v0.0.5 h1:kq9mnHZkih9Jl4DyIJ4Rzt++Y3DDKy3nI8S2ESEfZ5w=
|
||||
github.com/kerberos-io/onvif v0.0.5/go.mod h1:Hr2dJOH2LM5SpYKk17gYZ1CMjhGhUl+QlT5kwYogrW0=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
|
||||
github.com/klauspost/compress v1.15.0 h1:xqfchp4whNFxn5A4XFyyYtitiWI8Hy5EW59jEwcyL6U=
|
||||
github.com/klauspost/compress v1.15.0/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
|
||||
github.com/klauspost/cpuid v1.2.3 h1:CCtW0xUnWGVINKvE/WWOYKdsPV6mawAtvQuSl8guwQs=
|
||||
github.com/klauspost/cpuid v1.2.3/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
@@ -305,6 +313,7 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/nsmith5/mjpeg v0.0.0-20200913181537-54b8ada0e53e h1:bQo/jQ9qvcw7zqnovm8IbLsaOq3F+ELUQcxtxvalQvA=
|
||||
github.com/nsmith5/mjpeg v0.0.0-20200913181537-54b8ada0e53e/go.mod h1:PW9xCZScEClMBP22n37i0SnN/8B9YzNXTNvOaIkLjv0=
|
||||
@@ -437,6 +446,7 @@ github.com/tidwall/gjson v1.14.3 h1:9jvXn7olKEHU1S9vwoMGliaT8jq1vJ7IH/n9zD9Dnlw=
|
||||
github.com/tidwall/gjson v1.14.3/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
|
||||
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
|
||||
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
|
||||
github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
|
||||
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
||||
github.com/tinylib/msgp v1.1.6 h1:i+SbKraHhnrf9M5MYmvQhFnbLhAXSDWF8WWsuyRdocw=
|
||||
@@ -447,6 +457,14 @@ github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLY
|
||||
github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0=
|
||||
github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY=
|
||||
github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
|
||||
github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
|
||||
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
|
||||
github.com/xdg-go/scram v1.0.2 h1:akYIkZ28e6A96dkWNJQu3nmCzH3YfwMPQExUYDaRv7w=
|
||||
github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs=
|
||||
github.com/xdg-go/stringprep v1.0.2 h1:6iq84/ryjjeRmMJwxutI51F2GIPlP5BfTvXHeYjyhBc=
|
||||
github.com/xdg-go/stringprep v1.0.2/go.mod h1:8F9zXuvzgwmyT5DUm4GUfZGDdT3W+LCvS6+da4O5kxM=
|
||||
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d h1:splanxYIlg+5LfHAM6xpdFEAYOk8iySO56hMFq6uLyA=
|
||||
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA=
|
||||
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
@@ -455,6 +473,8 @@ github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
github.com/ziutek/mymysql v1.5.4 h1:GB0qdRGsTwQSBVYuVShFBKaXSnSnYYC2d9knnE1LHFs=
|
||||
github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0=
|
||||
go.mongodb.org/mongo-driver v1.7.5 h1:ny3p0reEpgsR2cfA5cjgwFZg3Cv/ofFh/8jbhGtz9VI=
|
||||
go.mongodb.org/mongo-driver v1.7.5/go.mod h1:VXEWRZ6URJIkUq2SCAyapmhH0ZLRBP+FT4xhp5Zvxng=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
@@ -470,6 +490,7 @@ golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8U
|
||||
golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
@@ -649,6 +670,7 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
@@ -670,6 +692,7 @@ golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3
|
||||
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
@@ -808,8 +831,6 @@ gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/ini.v1 v1.42.0 h1:7N3gPTt50s8GuLortA00n8AqRTk75qOP98+mTPpgzRk=
|
||||
gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22 h1:VpOs+IwYnYBaFnrNAeB8UUWtL3vEUnzSCL1nVjPhqrw=
|
||||
gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
@@ -16,7 +18,6 @@ import (
|
||||
var VERSION = "3.0.0"
|
||||
|
||||
func main() {
|
||||
|
||||
// You might be interested in debugging the agent.
|
||||
if os.Getenv("DATADOG_AGENT_ENABLED") == "true" {
|
||||
if os.Getenv("DATADOG_AGENT_K8S_ENABLED") == "true" {
|
||||
@@ -49,7 +50,20 @@ func main() {
|
||||
}
|
||||
|
||||
// Start the show ;)
|
||||
action := os.Args[1]
|
||||
// We'll parse the flags (named variables), and start the agent.
|
||||
|
||||
var action string
|
||||
var configDirectory string
|
||||
var name string
|
||||
var port string
|
||||
var timeout string
|
||||
|
||||
flag.StringVar(&action, "action", "version", "Tell us what you want do 'run' or 'version'")
|
||||
flag.StringVar(&configDirectory, "config", ".", "Where is the configuration stored")
|
||||
flag.StringVar(&name, "name", "agent", "Provide a name for the agent")
|
||||
flag.StringVar(&port, "port", "80", "On which port should the agent run")
|
||||
flag.StringVar(&timeout, "timeout", "2000", "Number of milliseconds to wait for the ONVIF discovery to complete")
|
||||
flag.Parse()
|
||||
|
||||
timezone, _ := time.LoadLocation("CET")
|
||||
log.Log.Init(timezone)
|
||||
@@ -60,14 +74,10 @@ func main() {
|
||||
log.Log.Info("You are currrently running Kerberos Agent " + VERSION)
|
||||
|
||||
case "discover":
|
||||
timeout := os.Args[2]
|
||||
log.Log.Info(timeout)
|
||||
|
||||
case "run":
|
||||
{
|
||||
name := os.Args[2]
|
||||
port := os.Args[3]
|
||||
|
||||
// Print Kerberos.io ASCII art
|
||||
utils.PrintASCIIArt()
|
||||
|
||||
@@ -82,7 +92,7 @@ func main() {
|
||||
configuration.Port = port
|
||||
|
||||
// Open this configuration either from Kerberos Agent or Kerberos Factory.
|
||||
components.OpenConfig(&configuration)
|
||||
components.OpenConfig(configDirectory, &configuration)
|
||||
|
||||
// We will override the configuration with the environment variables
|
||||
components.OverrideWithEnvironmentVariables(&configuration)
|
||||
@@ -103,7 +113,7 @@ func main() {
|
||||
if configuration.Config.Key == "" {
|
||||
key := utils.RandStringBytesMaskImpr(30)
|
||||
configuration.Config.Key = key
|
||||
err := components.StoreConfig(configuration.Config)
|
||||
err := components.StoreConfig(configDirectory, configuration.Config)
|
||||
if err == nil {
|
||||
log.Log.Info("Main: updated unique key for agent to: " + key)
|
||||
} else {
|
||||
@@ -111,14 +121,20 @@ func main() {
|
||||
}
|
||||
}
|
||||
|
||||
// Create a cancelable context, which will be used to cancel and restart.
|
||||
// This is used to restart the agent when the configuration is updated.
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
|
||||
// Bootstrapping the agent
|
||||
communication := models.Communication{
|
||||
Context: &ctx,
|
||||
CancelContext: &cancel,
|
||||
HandleBootstrap: make(chan string, 1),
|
||||
}
|
||||
go components.Bootstrap(&configuration, &communication)
|
||||
go components.Bootstrap(configDirectory, &configuration, &communication)
|
||||
|
||||
// Start the REST API.
|
||||
routers.StartWebserver(&configuration, &communication)
|
||||
routers.StartWebserver(configDirectory, &configuration, &communication)
|
||||
}
|
||||
default:
|
||||
log.Log.Error("Main: Sorry I don't understand :(")
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package capture
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
@@ -15,9 +16,9 @@ import (
|
||||
"github.com/kerberos-io/joy4/format"
|
||||
)
|
||||
|
||||
func OpenRTSP(url string) (av.DemuxCloser, []av.CodecData, error) {
|
||||
func OpenRTSP(ctx context.Context, url string) (av.DemuxCloser, []av.CodecData, error) {
|
||||
format.RegisterAll()
|
||||
infile, err := avutil.Open(url)
|
||||
infile, err := avutil.Open(ctx, url)
|
||||
if err == nil {
|
||||
streams, errstreams := infile.Streams()
|
||||
return infile, streams, errstreams
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
package capture
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"strconv"
|
||||
"time"
|
||||
@@ -431,6 +432,10 @@ func VerifyCamera(c *gin.Context) {
|
||||
var cameraStreams models.CameraStreams
|
||||
err := c.BindJSON(&cameraStreams)
|
||||
|
||||
// Should return in 5 seconds.
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
if err == nil {
|
||||
|
||||
streamType := c.Param("streamType")
|
||||
@@ -442,7 +447,7 @@ func VerifyCamera(c *gin.Context) {
|
||||
if streamType == "secondary" {
|
||||
rtspUrl = cameraStreams.SubRTSP
|
||||
}
|
||||
_, codecs, err := OpenRTSP(rtspUrl)
|
||||
_, codecs, err := OpenRTSP(ctx, rtspUrl)
|
||||
if err == nil {
|
||||
|
||||
videoIdx := -1
|
||||
|
||||
@@ -15,14 +15,12 @@ import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/golang-module/carbon/v2"
|
||||
"github.com/kerberos-io/joy4/av/pubsub"
|
||||
"github.com/minio/minio-go/v6"
|
||||
|
||||
mqtt "github.com/eclipse/paho.mqtt.golang"
|
||||
av "github.com/kerberos-io/joy4/av"
|
||||
"github.com/kerberos-io/joy4/cgo/ffmpeg"
|
||||
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
@@ -85,9 +83,9 @@ func HandleUpload(configuration *models.Configuration, communication *models.Com
|
||||
uploaded := false
|
||||
configured := false
|
||||
err = nil
|
||||
if config.Cloud == "s3" {
|
||||
uploaded, configured, err = UploadS3(configuration, fileName)
|
||||
} else if config.Cloud == "kstorage" {
|
||||
if config.Cloud == "s3" || config.Cloud == "kerberoshub" {
|
||||
uploaded, configured, err = UploadKerberosHub(configuration, fileName)
|
||||
} else if config.Cloud == "kstorage" || config.Cloud == "kerberosvault" {
|
||||
uploaded, configured, err = UploadKerberosVault(configuration, fileName)
|
||||
} else if config.Cloud == "dropbox" {
|
||||
uploaded, configured, err = UploadDropbox(configuration, fileName)
|
||||
@@ -103,6 +101,13 @@ func HandleUpload(configuration *models.Configuration, communication *models.Com
|
||||
// Todo: implement ftp upload
|
||||
} else if config.Cloud == "sftp" {
|
||||
// Todo: implement sftp upload
|
||||
} else if config.Cloud == "aws" {
|
||||
// Todo: need to be updated, was previously used for hub.
|
||||
uploaded, configured, err = UploadS3(configuration, fileName)
|
||||
} else if config.Cloud == "azure" {
|
||||
// Todo: implement azure upload
|
||||
} else if config.Cloud == "google" {
|
||||
// Todo: implement google upload
|
||||
}
|
||||
// And so on... (have a look here -> https://github.com/kerberos-io/agent/issues/95)
|
||||
|
||||
@@ -272,13 +277,21 @@ loop:
|
||||
|
||||
// We'll check which mode is enabled for the camera.
|
||||
onvifEnabled := "false"
|
||||
onvifZoom := "false"
|
||||
onvifPanTilt := "false"
|
||||
if config.Capture.IPCamera.ONVIFXAddr != "" {
|
||||
device, err := onvif.ConnectToOnvifDevice(configuration)
|
||||
cameraConfiguration := configuration.Config.Capture.IPCamera
|
||||
device, err := onvif.ConnectToOnvifDevice(&cameraConfiguration)
|
||||
if err == nil {
|
||||
capabilities := onvif.GetCapabilitiesFromDevice(device)
|
||||
for _, v := range capabilities {
|
||||
if v == "PTZ" || v == "ptz" {
|
||||
onvifEnabled = "true"
|
||||
configurations, err := onvif.GetPTZConfigurationsFromDevice(device)
|
||||
if err == nil {
|
||||
onvifEnabled = "true"
|
||||
_, canZoom, canPanTilt := onvif.GetPTZFunctionsFromDevice(configurations)
|
||||
if canZoom {
|
||||
onvifZoom = "true"
|
||||
}
|
||||
if canPanTilt {
|
||||
onvifPanTilt = "true"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -324,6 +337,8 @@ loop:
|
||||
"boot_time" : "%s",
|
||||
"siteID" : "%s",
|
||||
"onvif" : "%s",
|
||||
"onvif_zoom" : "%s",
|
||||
"onvif_pantilt" : "%s",
|
||||
"cameraConnected": "%s",
|
||||
"numberoffiles" : "33",
|
||||
"timestamp" : 1564747908,
|
||||
@@ -331,14 +346,23 @@ loop:
|
||||
"docker" : true,
|
||||
"kios" : false,
|
||||
"raspberrypi" : false
|
||||
}`, config.Key, system.Version, system.CPUId, username, key, name, isEnterprise, system.Hostname, system.Architecture, system.TotalMemory, system.UsedMemory, system.FreeMemory, system.ProcessUsedMemory, macs, ips, "0", "0", "0", uptimeString, boottimeString, config.HubSite, onvifEnabled, cameraConnected)
|
||||
}`, config.Key, system.Version, system.CPUId, username, key, name, isEnterprise, system.Hostname, system.Architecture, system.TotalMemory, system.UsedMemory, system.FreeMemory, system.ProcessUsedMemory, macs, ips, "0", "0", "0", uptimeString, boottimeString, config.HubSite, onvifEnabled, onvifZoom, onvifPanTilt, cameraConnected)
|
||||
|
||||
var jsonStr = []byte(object)
|
||||
buffy := bytes.NewBuffer(jsonStr)
|
||||
req, _ := http.NewRequest("POST", url, buffy)
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
client := &http.Client{}
|
||||
var client *http.Client
|
||||
if os.Getenv("AGENT_TLS_INSECURE") == "true" {
|
||||
tr := &http.Transport{
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
||||
}
|
||||
client = &http.Client{Transport: tr}
|
||||
} else {
|
||||
client = &http.Client{}
|
||||
}
|
||||
|
||||
resp, err := client.Do(req)
|
||||
if resp != nil {
|
||||
resp.Body.Close()
|
||||
@@ -347,6 +371,9 @@ loop:
|
||||
communication.CloudTimestamp.Store(time.Now().Unix())
|
||||
log.Log.Info("HandleHeartBeat: (200) Heartbeat received by Kerberos Hub.")
|
||||
} else {
|
||||
if communication.CloudTimestamp != nil && communication.CloudTimestamp.Load() != nil {
|
||||
communication.CloudTimestamp.Store(int64(0))
|
||||
}
|
||||
log.Log.Error("HandleHeartBeat: (400) Something went wrong while sending to Kerberos Hub.")
|
||||
}
|
||||
|
||||
@@ -357,8 +384,6 @@ loop:
|
||||
buffy = bytes.NewBuffer(jsonStr)
|
||||
req, _ = http.NewRequest("POST", vaultURI+"/devices/heartbeat", buffy)
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
client = &http.Client{}
|
||||
resp, err = client.Do(req)
|
||||
if resp != nil {
|
||||
resp.Body.Close()
|
||||
@@ -525,15 +550,23 @@ func VerifyHub(c *gin.Context) {
|
||||
err := c.BindJSON(&config)
|
||||
|
||||
if err == nil {
|
||||
hubKey := config.HubKey
|
||||
hubURI := config.HubURI
|
||||
publicKey := config.HubKey
|
||||
privateKey := config.HubPrivateKey
|
||||
|
||||
content := []byte(`{"message": "fake-message"}`)
|
||||
body := bytes.NewReader(content)
|
||||
req, err := http.NewRequest("POST", hubURI+"/queue/test", body)
|
||||
req, err := http.NewRequest("POST", hubURI+"/subscription/verify", nil)
|
||||
if err == nil {
|
||||
req.Header.Set("X-Kerberos-Cloud-Key", hubKey)
|
||||
client := &http.Client{}
|
||||
req.Header.Set("X-Kerberos-Hub-PublicKey", publicKey)
|
||||
req.Header.Set("X-Kerberos-Hub-PrivateKey", privateKey)
|
||||
var client *http.Client
|
||||
if os.Getenv("AGENT_TLS_INSECURE") == "true" {
|
||||
tr := &http.Transport{
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
||||
}
|
||||
client = &http.Client{Transport: tr}
|
||||
} else {
|
||||
client = &http.Client{}
|
||||
}
|
||||
|
||||
resp, err := client.Do(req)
|
||||
if err == nil {
|
||||
@@ -589,88 +622,88 @@ func VerifyPersistence(c *gin.Context) {
|
||||
|
||||
if config.Cloud == "dropbox" {
|
||||
VerifyDropbox(config, c)
|
||||
} else if config.Cloud == "s3" {
|
||||
} else if config.Cloud == "s3" || config.Cloud == "kerberoshub" {
|
||||
|
||||
// timestamp_microseconds_instanceName_regionCoordinates_numberOfChanges_token
|
||||
// 1564859471_6-474162_oprit_577-283-727-375_1153_27.mp4
|
||||
// - Timestamp
|
||||
// - Size + - + microseconds
|
||||
// - device
|
||||
// - Region
|
||||
// - Number of changes
|
||||
// - Token
|
||||
|
||||
aws_access_key_id := config.S3.Publickey
|
||||
aws_secret_access_key := config.S3.Secretkey
|
||||
aws_region := config.S3.Region
|
||||
|
||||
// This is the new way ;)
|
||||
if config.HubKey != "" {
|
||||
aws_access_key_id = config.HubKey
|
||||
}
|
||||
if config.HubPrivateKey != "" {
|
||||
aws_secret_access_key = config.HubPrivateKey
|
||||
}
|
||||
|
||||
s3Client, err := minio.NewWithRegion("s3.amazonaws.com", aws_access_key_id, aws_secret_access_key, true, aws_region)
|
||||
if err != nil {
|
||||
if config.HubURI == "" ||
|
||||
config.HubKey == "" ||
|
||||
config.HubPrivateKey == "" ||
|
||||
config.S3.Region == "" {
|
||||
msg := "VerifyPersistence: Kerberos Hub not properly configured."
|
||||
log.Log.Info(msg)
|
||||
c.JSON(400, models.APIResponse{
|
||||
Data: "Creation of Kerberos Hub connection failed: " + err.Error(),
|
||||
Data: msg,
|
||||
})
|
||||
} else {
|
||||
|
||||
// Check if we need to use the proxy.
|
||||
if config.S3.ProxyURI != "" {
|
||||
var transport http.RoundTripper = &http.Transport{
|
||||
Proxy: func(*http.Request) (*url.URL, error) {
|
||||
return url.Parse(config.S3.ProxyURI)
|
||||
},
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
||||
}
|
||||
s3Client.SetCustomTransport(transport)
|
||||
// Open test-480p.mp4
|
||||
file, err := os.Open("./data/test-480p.mp4")
|
||||
if err != nil {
|
||||
msg := "VerifyPersistence: error reading test-480p.mp4: " + err.Error()
|
||||
log.Log.Error(msg)
|
||||
c.JSON(400, models.APIResponse{
|
||||
Data: msg,
|
||||
})
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
req, err := http.NewRequest("POST", config.HubURI+"/storage/upload", file)
|
||||
if err != nil {
|
||||
msg := "VerifyPersistence: error reading Kerberos Hub HEAD request, " + config.HubURI + "/storage: " + err.Error()
|
||||
log.Log.Error(msg)
|
||||
c.JSON(400, models.APIResponse{
|
||||
Data: msg,
|
||||
})
|
||||
}
|
||||
|
||||
deviceKey := "fake-key"
|
||||
devicename := "justatest"
|
||||
coordinates := "200-200-400-400"
|
||||
eventToken := "769"
|
||||
|
||||
timestamp := time.Now().Unix()
|
||||
fileName := strconv.FormatInt(timestamp, 10) + "_6-967003_justatest_200-200-400-400_24_769.mp4"
|
||||
content := []byte("test-file")
|
||||
body := bytes.NewReader(content)
|
||||
fileName := strconv.FormatInt(timestamp, 10) +
|
||||
"_6-967003_" + config.Name + "_200-200-400-400_24_769.mp4"
|
||||
req.Header.Set("X-Kerberos-Storage-FileName", fileName)
|
||||
req.Header.Set("X-Kerberos-Storage-Capture", "IPCamera")
|
||||
req.Header.Set("X-Kerberos-Storage-Device", config.Key)
|
||||
req.Header.Set("X-Kerberos-Hub-PublicKey", config.HubKey)
|
||||
req.Header.Set("X-Kerberos-Hub-PrivateKey", config.HubPrivateKey)
|
||||
req.Header.Set("X-Kerberos-Hub-Region", config.S3.Region)
|
||||
|
||||
n, err := s3Client.PutObject(config.S3.Bucket,
|
||||
config.S3.Username+"/"+fileName,
|
||||
body,
|
||||
body.Size(),
|
||||
minio.PutObjectOptions{
|
||||
ContentType: "video/mp4",
|
||||
StorageClass: "ONEZONE_IA",
|
||||
UserMetadata: map[string]string{
|
||||
"event-timestamp": strconv.FormatInt(timestamp, 10),
|
||||
"event-microseconds": deviceKey,
|
||||
"event-instancename": devicename,
|
||||
"event-regioncoordinates": coordinates,
|
||||
"event-numberofchanges": deviceKey,
|
||||
"event-token": eventToken,
|
||||
"productid": deviceKey,
|
||||
"publickey": aws_access_key_id,
|
||||
"uploadtime": "now",
|
||||
},
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
c.JSON(400, models.APIResponse{
|
||||
Data: "Upload of fake recording failed: " + err.Error(),
|
||||
})
|
||||
var client *http.Client
|
||||
if os.Getenv("AGENT_TLS_INSECURE") == "true" {
|
||||
tr := &http.Transport{
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
||||
}
|
||||
client = &http.Client{Transport: tr}
|
||||
} else {
|
||||
c.JSON(200, models.APIResponse{
|
||||
Data: "Upload Finished: file has been uploaded to bucket: " + strconv.FormatInt(n, 10),
|
||||
client = &http.Client{}
|
||||
}
|
||||
|
||||
resp, err := client.Do(req)
|
||||
if resp != nil {
|
||||
defer resp.Body.Close()
|
||||
}
|
||||
|
||||
if err == nil && resp != nil {
|
||||
if resp.StatusCode == 200 {
|
||||
msg := "VerifyPersistence: Upload allowed using the credentials provided (" + config.HubKey + ", " + config.HubPrivateKey + ")"
|
||||
log.Log.Info(msg)
|
||||
c.JSON(200, models.APIResponse{
|
||||
Data: msg,
|
||||
})
|
||||
} else {
|
||||
msg := "VerifyPersistence: Upload NOT allowed using the credentials provided (" + config.HubKey + ", " + config.HubPrivateKey + ")"
|
||||
log.Log.Info(msg)
|
||||
c.JSON(400, models.APIResponse{
|
||||
Data: msg,
|
||||
})
|
||||
}
|
||||
} else {
|
||||
msg := "VerifyPersistence: Error creating Kerberos Hub request"
|
||||
log.Log.Info(msg)
|
||||
c.JSON(400, models.APIResponse{
|
||||
Data: msg,
|
||||
})
|
||||
}
|
||||
}
|
||||
} else if config.Cloud == "kstorage" {
|
||||
|
||||
} else if config.Cloud == "kstorage" || config.Cloud == "kerberosvault" {
|
||||
|
||||
uri := config.KStorage.URI
|
||||
accessKey := config.KStorage.AccessKey
|
||||
@@ -679,10 +712,18 @@ func VerifyPersistence(c *gin.Context) {
|
||||
provider := config.KStorage.Provider
|
||||
|
||||
if err == nil && uri != "" && accessKey != "" && secretAccessKey != "" {
|
||||
var postData = []byte(`{"title":"Buy cheese and bread for breakfast."}`)
|
||||
client := &http.Client{}
|
||||
req, err := http.NewRequest("POST", uri+"/ping", bytes.NewReader(postData))
|
||||
|
||||
var client *http.Client
|
||||
if os.Getenv("AGENT_TLS_INSECURE") == "true" {
|
||||
tr := &http.Transport{
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
||||
}
|
||||
client = &http.Client{Transport: tr}
|
||||
} else {
|
||||
client = &http.Client{}
|
||||
}
|
||||
|
||||
req, err := http.NewRequest("POST", uri+"/ping", nil)
|
||||
req.Header.Add("X-Kerberos-Storage-AccessKey", accessKey)
|
||||
req.Header.Add("X-Kerberos-Storage-SecretAccessKey", secretAccessKey)
|
||||
resp, err := client.Do(req)
|
||||
@@ -694,33 +735,44 @@ func VerifyPersistence(c *gin.Context) {
|
||||
|
||||
if provider != "" || directory != "" {
|
||||
|
||||
hubKey := config.KStorage.CloudKey
|
||||
// This is the new way ;)
|
||||
if config.HubKey != "" {
|
||||
hubKey = config.HubKey
|
||||
}
|
||||
|
||||
// Generate a random name.
|
||||
timestamp := time.Now().Unix()
|
||||
fileName := strconv.FormatInt(timestamp, 10) +
|
||||
"_6-967003_justatest_200-200-400-400_24_769.mp4"
|
||||
content := []byte("test-file")
|
||||
body := bytes.NewReader(content)
|
||||
//fileSize := int64(len(content))
|
||||
"_6-967003_" + config.Name + "_200-200-400-400_24_769.mp4"
|
||||
|
||||
req, err := http.NewRequest("POST", uri+"/storage", body)
|
||||
// Open test-480p.mp4
|
||||
file, err := os.Open("./data/test-480p.mp4")
|
||||
if err != nil {
|
||||
msg := "VerifyPersistence: error reading test-480p.mp4: " + err.Error()
|
||||
log.Log.Error(msg)
|
||||
c.JSON(400, models.APIResponse{
|
||||
Data: msg,
|
||||
})
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
req, err := http.NewRequest("POST", uri+"/storage", file)
|
||||
if err == nil {
|
||||
|
||||
req.Header.Set("Content-Type", "video/mp4")
|
||||
req.Header.Set("X-Kerberos-Storage-CloudKey", hubKey)
|
||||
req.Header.Set("X-Kerberos-Storage-CloudKey", config.HubKey)
|
||||
req.Header.Set("X-Kerberos-Storage-AccessKey", accessKey)
|
||||
req.Header.Set("X-Kerberos-Storage-SecretAccessKey", secretAccessKey)
|
||||
req.Header.Set("X-Kerberos-Storage-Provider", provider)
|
||||
req.Header.Set("X-Kerberos-Storage-FileName", fileName)
|
||||
req.Header.Set("X-Kerberos-Storage-Device", "test")
|
||||
req.Header.Set("X-Kerberos-Storage-Device", config.Key)
|
||||
req.Header.Set("X-Kerberos-Storage-Capture", "IPCamera")
|
||||
req.Header.Set("X-Kerberos-Storage-Directory", directory)
|
||||
client := &http.Client{}
|
||||
|
||||
var client *http.Client
|
||||
if os.Getenv("AGENT_TLS_INSECURE") == "true" {
|
||||
tr := &http.Transport{
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
||||
}
|
||||
client = &http.Client{Transport: tr}
|
||||
} else {
|
||||
client = &http.Client{}
|
||||
}
|
||||
|
||||
resp, err := client.Do(req)
|
||||
|
||||
@@ -733,41 +785,45 @@ func VerifyPersistence(c *gin.Context) {
|
||||
c.JSON(200, body)
|
||||
} else {
|
||||
c.JSON(400, models.APIResponse{
|
||||
Data: "Something went wrong while verifying your persistence settings. Make sure your provider is the same as the storage provider in your Kerberos Vault, and the relevant storage provider is configured properly.",
|
||||
Data: "VerifyPersistence: Something went wrong while verifying your persistence settings. Make sure your provider is the same as the storage provider in your Kerberos Vault, and the relevant storage provider is configured properly.",
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
c.JSON(400, models.APIResponse{
|
||||
Data: "Upload of fake recording failed: " + err.Error(),
|
||||
Data: "VerifyPersistence: Upload of fake recording failed: " + err.Error(),
|
||||
})
|
||||
}
|
||||
} else {
|
||||
c.JSON(400, models.APIResponse{
|
||||
Data: "Something went wrong while creating /storage POST request." + err.Error(),
|
||||
Data: "VerifyPersistence: Something went wrong while creating /storage POST request." + err.Error(),
|
||||
})
|
||||
}
|
||||
} else {
|
||||
c.JSON(400, models.APIResponse{
|
||||
Data: "Provider and/or directory is missing from the request.",
|
||||
Data: "VerifyPersistence: Provider and/or directory is missing from the request.",
|
||||
})
|
||||
}
|
||||
} else {
|
||||
c.JSON(400, models.APIResponse{
|
||||
Data: "Something went wrong while verifying storage credentials: " + string(body),
|
||||
Data: "VerifyPersistence: Something went wrong while verifying storage credentials: " + string(body),
|
||||
})
|
||||
}
|
||||
} else {
|
||||
c.JSON(400, models.APIResponse{
|
||||
Data: "Something went wrong while verifying storage credentials:" + err.Error(),
|
||||
Data: "VerifyPersistence: Something went wrong while verifying storage credentials:" + err.Error(),
|
||||
})
|
||||
}
|
||||
} else {
|
||||
c.JSON(400, models.APIResponse{
|
||||
Data: "VerifyPersistence: please fill-in the required Kerberos Vault credentials.",
|
||||
})
|
||||
}
|
||||
}
|
||||
} else {
|
||||
c.JSON(400, models.APIResponse{
|
||||
Data: "No persistence was specified, so do not know what to verify:" + err.Error(),
|
||||
Data: "VerifyPersistence: No persistence was specified, so do not know what to verify:" + err.Error(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
131
machinery/src/cloud/KerberosHub.go
Normal file
131
machinery/src/cloud/KerberosHub.go
Normal file
@@ -0,0 +1,131 @@
|
||||
package cloud
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
"github.com/kerberos-io/agent/machinery/src/log"
|
||||
"github.com/kerberos-io/agent/machinery/src/models"
|
||||
)
|
||||
|
||||
func UploadKerberosHub(configuration *models.Configuration, fileName string) (bool, bool, error) {
|
||||
config := configuration.Config
|
||||
|
||||
if config.HubURI == "" ||
|
||||
config.HubKey == "" ||
|
||||
config.HubPrivateKey == "" ||
|
||||
config.S3.Region == "" {
|
||||
err := "UploadKerberosHub: Kerberos Hub not properly configured."
|
||||
log.Log.Info(err)
|
||||
return false, false, errors.New(err)
|
||||
}
|
||||
|
||||
// timestamp_microseconds_instanceName_regionCoordinates_numberOfChanges_token
|
||||
// 1564859471_6-474162_oprit_577-283-727-375_1153_27.mp4
|
||||
// - Timestamp
|
||||
// - Size + - + microseconds
|
||||
// - device
|
||||
// - Region
|
||||
// - Number of changes
|
||||
// - Token
|
||||
|
||||
log.Log.Info("UploadKerberosHub: Uploading to Kerberos Hub (" + config.HubURI + ")")
|
||||
log.Log.Info("UploadKerberosHub: Upload started for " + fileName)
|
||||
fullname := "data/recordings/" + fileName
|
||||
|
||||
// Check if we still have the file otherwise we abort the request.
|
||||
file, err := os.OpenFile(fullname, os.O_RDWR, 0755)
|
||||
if file != nil {
|
||||
defer file.Close()
|
||||
}
|
||||
if err != nil {
|
||||
err := "UploadKerberosHub: Upload Failed, file doesn't exists anymore."
|
||||
log.Log.Info(err)
|
||||
return false, true, errors.New(err)
|
||||
}
|
||||
|
||||
// Check if we are allowed to upload to the hub with these credentials.
|
||||
// There might be different reasons like (muted, read-only..)
|
||||
req, err := http.NewRequest("HEAD", config.HubURI+"/storage/upload", nil)
|
||||
if err != nil {
|
||||
errorMessage := "UploadKerberosHub: error reading HEAD request, " + config.HubURI + "/storage: " + err.Error()
|
||||
log.Log.Error(errorMessage)
|
||||
return false, true, errors.New(errorMessage)
|
||||
}
|
||||
|
||||
req.Header.Set("X-Kerberos-Storage-FileName", fileName)
|
||||
req.Header.Set("X-Kerberos-Storage-Capture", "IPCamera")
|
||||
req.Header.Set("X-Kerberos-Storage-Device", config.Key)
|
||||
req.Header.Set("X-Kerberos-Hub-PublicKey", config.HubKey)
|
||||
req.Header.Set("X-Kerberos-Hub-PrivateKey", config.HubPrivateKey)
|
||||
req.Header.Set("X-Kerberos-Hub-Region", config.S3.Region)
|
||||
|
||||
var client *http.Client
|
||||
if os.Getenv("AGENT_TLS_INSECURE") == "true" {
|
||||
tr := &http.Transport{
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
||||
}
|
||||
client = &http.Client{Transport: tr}
|
||||
} else {
|
||||
client = &http.Client{}
|
||||
}
|
||||
|
||||
resp, err := client.Do(req)
|
||||
if resp != nil {
|
||||
defer resp.Body.Close()
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
if resp != nil {
|
||||
if err == nil {
|
||||
if resp.StatusCode == 200 {
|
||||
log.Log.Info("UploadKerberosHub: Upload allowed using the credentials provided (" + config.HubKey + ", " + config.HubPrivateKey + ")")
|
||||
} else {
|
||||
log.Log.Info("UploadKerberosHub: Upload NOT allowed using the credentials provided (" + config.HubKey + ", " + config.HubPrivateKey + ")")
|
||||
return false, true, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Now we know we are allowed to upload to the hub, we can start uploading.
|
||||
req, err = http.NewRequest("POST", config.HubURI+"/storage/upload", file)
|
||||
if err != nil {
|
||||
errorMessage := "UploadKerberosHub: error reading POST request, " + config.KStorage.URI + "/storage/upload: " + err.Error()
|
||||
log.Log.Error(errorMessage)
|
||||
return false, true, errors.New(errorMessage)
|
||||
}
|
||||
req.Header.Set("Content-Type", "video/mp4")
|
||||
req.Header.Set("X-Kerberos-Storage-FileName", fileName)
|
||||
req.Header.Set("X-Kerberos-Storage-Capture", "IPCamera")
|
||||
req.Header.Set("X-Kerberos-Storage-Device", config.Key)
|
||||
req.Header.Set("X-Kerberos-Hub-PublicKey", config.HubKey)
|
||||
req.Header.Set("X-Kerberos-Hub-PrivateKey", config.HubPrivateKey)
|
||||
req.Header.Set("X-Kerberos-Hub-Region", config.S3.Region)
|
||||
resp, err = client.Do(req)
|
||||
if resp != nil {
|
||||
defer resp.Body.Close()
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
if resp != nil {
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err == nil {
|
||||
if resp.StatusCode == 200 {
|
||||
log.Log.Info("UploadKerberosHub: Upload Finished, " + resp.Status + ".")
|
||||
return true, true, nil
|
||||
} else {
|
||||
log.Log.Info("UploadKerberosHub: Upload Failed, " + resp.Status + ", " + string(body))
|
||||
return false, true, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
errorMessage := "UploadKerberosHub: Upload Failed, " + err.Error()
|
||||
log.Log.Info(errorMessage)
|
||||
return false, true, errors.New(errorMessage)
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package cloud
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
@@ -67,7 +68,16 @@ func UploadKerberosVault(configuration *models.Configuration, fileName string) (
|
||||
req.Header.Set("X-Kerberos-Storage-Device", config.Key)
|
||||
req.Header.Set("X-Kerberos-Storage-Capture", "IPCamera")
|
||||
req.Header.Set("X-Kerberos-Storage-Directory", config.KStorage.Directory)
|
||||
client := &http.Client{}
|
||||
|
||||
var client *http.Client
|
||||
if os.Getenv("AGENT_TLS_INSECURE") == "true" {
|
||||
tr := &http.Transport{
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
||||
}
|
||||
client = &http.Client{Transport: tr}
|
||||
} else {
|
||||
client = &http.Client{}
|
||||
}
|
||||
|
||||
resp, err := client.Do(req)
|
||||
if resp != nil {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package components
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"image"
|
||||
@@ -16,7 +17,7 @@ import (
|
||||
"github.com/kerberos-io/agent/machinery/src/database"
|
||||
"github.com/kerberos-io/agent/machinery/src/log"
|
||||
"github.com/kerberos-io/agent/machinery/src/models"
|
||||
"gopkg.in/mgo.v2/bson"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
)
|
||||
|
||||
func GetImageFromFilePath() (image.Image, error) {
|
||||
@@ -41,11 +42,11 @@ func GetImageFromFilePath() (image.Image, error) {
|
||||
// ReadUserConfig Reads the user configuration of the Kerberos Open Source instance.
|
||||
// This will return a models.User struct including the username, password,
|
||||
// selected language, and if the installation was completed or not.
|
||||
func ReadUserConfig() (userConfig models.User) {
|
||||
func ReadUserConfig(configDirectory string) (userConfig models.User) {
|
||||
for {
|
||||
jsonFile, err := os.Open("./data/config/user.json")
|
||||
if err != nil {
|
||||
log.Log.Error("Config file is not found " + "./data/config/user.json, trying again in 5s: " + err.Error())
|
||||
log.Log.Error("Config file is not found " + configDirectory + "/data/config/user.json, trying again in 5s: " + err.Error())
|
||||
time.Sleep(5 * time.Second)
|
||||
} else {
|
||||
log.Log.Info("Successfully Opened user.json")
|
||||
@@ -65,7 +66,7 @@ func ReadUserConfig() (userConfig models.User) {
|
||||
return
|
||||
}
|
||||
|
||||
func OpenConfig(configuration *models.Configuration) {
|
||||
func OpenConfig(configDirectory string, configuration *models.Configuration) {
|
||||
|
||||
// We are checking which deployment this is running, so we can load
|
||||
// into the configuration as expected.
|
||||
@@ -76,19 +77,31 @@ func OpenConfig(configuration *models.Configuration) {
|
||||
// Multiple agents have there configuration stored, and can benefit from
|
||||
// the concept of a global concept.
|
||||
|
||||
session := database.New().Copy()
|
||||
defer session.Close()
|
||||
db := session.DB(database.DatabaseName)
|
||||
collection := db.C("configuration")
|
||||
// Write to mongodb
|
||||
client := database.New()
|
||||
|
||||
collection.Find(bson.M{
|
||||
db := client.Database(database.DatabaseName)
|
||||
collection := db.Collection("configuration")
|
||||
|
||||
var globalConfig models.Config
|
||||
err := collection.FindOne(context.Background(), bson.M{
|
||||
"type": "global",
|
||||
}).One(&configuration.GlobalConfig)
|
||||
}).Decode(&globalConfig)
|
||||
if err != nil {
|
||||
log.Log.Error("Could not find global configuration, using default configuration.")
|
||||
}
|
||||
configuration.GlobalConfig = globalConfig
|
||||
|
||||
collection.Find(bson.M{
|
||||
var customConfig models.Config
|
||||
deploymentName := os.Getenv("DEPLOYMENT_NAME")
|
||||
err = collection.FindOne(context.Background(), bson.M{
|
||||
"type": "config",
|
||||
"name": os.Getenv("DEPLOYMENT_NAME"),
|
||||
}).One(&configuration.CustomConfig)
|
||||
"name": deploymentName,
|
||||
}).Decode(&customConfig)
|
||||
if err != nil {
|
||||
log.Log.Error("Could not find configuration for " + deploymentName + ", using global configuration.")
|
||||
}
|
||||
configuration.CustomConfig = customConfig
|
||||
|
||||
// We will merge both configs in a single config file.
|
||||
// Read again from database but this store overwrite the same object.
|
||||
@@ -133,9 +146,9 @@ func OpenConfig(configuration *models.Configuration) {
|
||||
|
||||
// Open device config
|
||||
for {
|
||||
jsonFile, err := os.Open("./data/config/config.json")
|
||||
jsonFile, err := os.Open(configDirectory + "/data/config/config.json")
|
||||
if err != nil {
|
||||
log.Log.Error("Config file is not found " + "./data/config/config.json" + ", trying again in 5s.")
|
||||
log.Log.Error("Config file is not found " + configDirectory + "/data/config/config.json" + ", trying again in 5s.")
|
||||
time.Sleep(5 * time.Second)
|
||||
} else {
|
||||
log.Log.Info("Successfully Opened config.json from " + configuration.Name)
|
||||
@@ -204,8 +217,7 @@ func OverrideWithEnvironmentVariables(configuration *models.Configuration) {
|
||||
|
||||
/* ONVIF connnection settings */
|
||||
case "AGENT_CAPTURE_IPCAMERA_ONVIF":
|
||||
isEnabled := value == " true"
|
||||
configuration.Config.Capture.IPCamera.ONVIF = isEnabled
|
||||
configuration.Config.Capture.IPCamera.ONVIF = value
|
||||
break
|
||||
case "AGENT_CAPTURE_IPCAMERA_ONVIF_XADDR":
|
||||
configuration.Config.Capture.IPCamera.ONVIFXAddr = value
|
||||
@@ -389,12 +401,12 @@ func OverrideWithEnvironmentVariables(configuration *models.Configuration) {
|
||||
case "AGENT_HUB_PRIVATE_KEY":
|
||||
configuration.Config.HubPrivateKey = value
|
||||
break
|
||||
case "AGENT_HUB_USERNAME":
|
||||
configuration.Config.S3.Username = value
|
||||
break
|
||||
case "AGENT_HUB_SITE":
|
||||
configuration.Config.HubSite = value
|
||||
break
|
||||
case "AGENT_HUB_REGION":
|
||||
configuration.Config.S3.Region = value
|
||||
break
|
||||
|
||||
/* When storing in a Kerberos Vault */
|
||||
case "AGENT_KERBEROSVAULT_URI":
|
||||
@@ -425,19 +437,21 @@ func OverrideWithEnvironmentVariables(configuration *models.Configuration) {
|
||||
}
|
||||
}
|
||||
|
||||
func SaveConfig(config models.Config, configuration *models.Configuration, communication *models.Communication) error {
|
||||
func SaveConfig(configDirectory string, config models.Config, configuration *models.Configuration, communication *models.Communication) error {
|
||||
if !communication.IsConfiguring.IsSet() {
|
||||
communication.IsConfiguring.Set()
|
||||
|
||||
err := StoreConfig(config)
|
||||
err := StoreConfig(configDirectory, config)
|
||||
if err != nil {
|
||||
communication.IsConfiguring.UnSet()
|
||||
return err
|
||||
}
|
||||
|
||||
select {
|
||||
case communication.HandleBootstrap <- "restart":
|
||||
default:
|
||||
if communication.CameraConnected {
|
||||
select {
|
||||
case communication.HandleBootstrap <- "restart":
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
communication.IsConfiguring.UnSet()
|
||||
@@ -448,25 +462,29 @@ func SaveConfig(config models.Config, configuration *models.Configuration, commu
|
||||
}
|
||||
}
|
||||
|
||||
func StoreConfig(config models.Config) error {
|
||||
func StoreConfig(configDirectory string, config models.Config) error {
|
||||
// Save into database
|
||||
if os.Getenv("DEPLOYMENT") == "factory" || os.Getenv("MACHINERY_ENVIRONMENT") == "kubernetes" {
|
||||
// Write to mongodb
|
||||
session := database.New().Copy()
|
||||
defer session.Close()
|
||||
db := session.DB(database.DatabaseName)
|
||||
collection := db.C("configuration")
|
||||
client := database.New()
|
||||
|
||||
err := collection.Update(bson.M{
|
||||
db := client.Database(database.DatabaseName)
|
||||
collection := db.Collection("configuration")
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
_, err := collection.UpdateOne(ctx, bson.M{
|
||||
"type": "config",
|
||||
"name": os.Getenv("DEPLOYMENT_NAME"),
|
||||
}, &config)
|
||||
}, bson.M{"$set": config})
|
||||
|
||||
return err
|
||||
|
||||
// Save into file
|
||||
} else if os.Getenv("DEPLOYMENT") == "" || os.Getenv("DEPLOYMENT") == "agent" {
|
||||
res, _ := json.MarshalIndent(config, "", "\t")
|
||||
err := ioutil.WriteFile("./data/config/config.json", res, 0644)
|
||||
err := ioutil.WriteFile(configDirectory+"/data/config/config.json", res, 0644)
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
package components
|
||||
|
||||
import (
|
||||
"context"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
mqtt "github.com/eclipse/paho.mqtt.golang"
|
||||
"github.com/kerberos-io/joy4/cgo/ffmpeg"
|
||||
|
||||
"github.com/kerberos-io/agent/machinery/src/capture"
|
||||
@@ -21,7 +23,7 @@ import (
|
||||
"github.com/tevino/abool"
|
||||
)
|
||||
|
||||
func Bootstrap(configuration *models.Configuration, communication *models.Communication) {
|
||||
func Bootstrap(configDirectory string, configuration *models.Configuration, communication *models.Communication) {
|
||||
log.Log.Debug("Bootstrap: started")
|
||||
|
||||
// We will keep track of the Kerberos Agent up time
|
||||
@@ -53,6 +55,7 @@ func Bootstrap(configuration *models.Configuration, communication *models.Commun
|
||||
communication.HandleLiveSD = make(chan int64, 1)
|
||||
communication.HandleLiveHDKeepalive = make(chan string, 1)
|
||||
communication.HandleLiveHDPeers = make(chan string, 1)
|
||||
communication.HandleONVIF = make(chan models.OnvifAction, 1)
|
||||
communication.IsConfiguring = abool.New()
|
||||
|
||||
// Before starting the agent, we have a control goroutine, that might
|
||||
@@ -67,33 +70,69 @@ func Bootstrap(configuration *models.Configuration, communication *models.Commun
|
||||
// Handle heartbeats
|
||||
go cloud.HandleHeartBeat(configuration, communication, uptimeStart)
|
||||
|
||||
// We'll create a MQTT handler, which will be used to communicate with Kerberos Hub.
|
||||
// Configure a MQTT client which helps for a bi-directional communication
|
||||
mqttClient := routers.ConfigureMQTT(configuration, communication)
|
||||
|
||||
// Run the agent and fire up all the other
|
||||
// goroutines which do image capture, motion detection, onvif, etc.
|
||||
|
||||
for {
|
||||
|
||||
// This will blocking until receiving a signal to be restarted, reconfigured, stopped, etc.
|
||||
status := RunAgent(configuration, communication, uptimeStart, cameraSettings, decoder, subDecoder)
|
||||
status := RunAgent(configDirectory, configuration, communication, mqttClient, uptimeStart, cameraSettings, decoder, subDecoder)
|
||||
|
||||
if status == "stop" {
|
||||
break
|
||||
}
|
||||
// We will re open the configuration, might have changed :O!
|
||||
OpenConfig(configuration)
|
||||
|
||||
// We will override the configuration with the environment variables
|
||||
OverrideWithEnvironmentVariables(configuration)
|
||||
if status == "not started" {
|
||||
// We will re open the configuration, might have changed :O!
|
||||
OpenConfig(configDirectory, configuration)
|
||||
// We will override the configuration with the environment variables
|
||||
OverrideWithEnvironmentVariables(configuration)
|
||||
}
|
||||
|
||||
// Reset the MQTT client, might have provided new information, so we need to reconnect.
|
||||
if routers.HasMQTTClientModified(configuration) {
|
||||
routers.DisconnectMQTT(mqttClient, &configuration.Config)
|
||||
mqttClient = routers.ConfigureMQTT(configuration, communication)
|
||||
}
|
||||
|
||||
// We will create a new cancelable context, which will be used to cancel and restart.
|
||||
// This is used to restart the agent when the configuration is updated.
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
communication.Context = &ctx
|
||||
communication.CancelContext = &cancel
|
||||
}
|
||||
log.Log.Debug("Bootstrap: finished")
|
||||
}
|
||||
|
||||
func RunAgent(configuration *models.Configuration, communication *models.Communication, uptimeStart time.Time, cameraSettings *models.Camera, decoder *ffmpeg.VideoDecoder, subDecoder *ffmpeg.VideoDecoder) string {
|
||||
func RunAgent(configDirectory string, configuration *models.Configuration, communication *models.Communication, mqttClient mqtt.Client, uptimeStart time.Time, cameraSettings *models.Camera, decoder *ffmpeg.VideoDecoder, subDecoder *ffmpeg.VideoDecoder) string {
|
||||
|
||||
log.Log.Debug("RunAgent: bootstrapping agent")
|
||||
config := configuration.Config
|
||||
|
||||
status := "not started"
|
||||
|
||||
// Currently only support H264 encoded cameras, this will change.
|
||||
// Establishing the camera connection
|
||||
rtspUrl := config.Capture.IPCamera.RTSP
|
||||
infile, streams, err := capture.OpenRTSP(rtspUrl)
|
||||
infile, streams, err := capture.OpenRTSP(context.Background(), rtspUrl)
|
||||
|
||||
// We will initialise the camera settings object
|
||||
// so we can check if the camera settings have changed, and we need
|
||||
// to reload the decoders.
|
||||
|
||||
videoStream, _ := capture.GetVideoStream(streams)
|
||||
if videoStream == nil {
|
||||
log.Log.Error("RunAgent: no video stream found, might be the wrong codec (we only support H264 for the moment)")
|
||||
time.Sleep(time.Second * 3)
|
||||
return status
|
||||
}
|
||||
|
||||
num, denum := videoStream.(av.VideoCodecData).Framerate()
|
||||
width := videoStream.(av.VideoCodecData).Width()
|
||||
height := videoStream.(av.VideoCodecData).Height()
|
||||
|
||||
var queue *pubsub.Queue
|
||||
var subQueue *pubsub.Queue
|
||||
@@ -101,11 +140,9 @@ func RunAgent(configuration *models.Configuration, communication *models.Communi
|
||||
var decoderMutex sync.Mutex
|
||||
var subDecoderMutex sync.Mutex
|
||||
|
||||
status := "not started"
|
||||
|
||||
if err == nil {
|
||||
|
||||
log.Log.Info("RunAgent: opened RTSP stream" + rtspUrl)
|
||||
log.Log.Info("RunAgent: opened RTSP stream: " + rtspUrl)
|
||||
|
||||
// We might have a secondary rtsp url, so we might need to use that.
|
||||
var subInfile av.DemuxCloser
|
||||
@@ -113,23 +150,21 @@ func RunAgent(configuration *models.Configuration, communication *models.Communi
|
||||
subStreamEnabled := false
|
||||
subRtspUrl := config.Capture.IPCamera.SubRTSP
|
||||
if subRtspUrl != "" && subRtspUrl != rtspUrl {
|
||||
subInfile, subStreams, err = capture.OpenRTSP(subRtspUrl)
|
||||
subInfile, subStreams, err = capture.OpenRTSP(context.Background(), subRtspUrl)
|
||||
if err == nil {
|
||||
log.Log.Info("RunAgent: opened RTSP sub stream " + subRtspUrl)
|
||||
subStreamEnabled = true
|
||||
}
|
||||
|
||||
videoStream, _ := capture.GetVideoStream(subStreams)
|
||||
if videoStream == nil {
|
||||
log.Log.Error("RunAgent: no video substream found, might be the wrong codec (we only support H264 for the moment)")
|
||||
time.Sleep(time.Second * 3)
|
||||
return status
|
||||
}
|
||||
}
|
||||
|
||||
// We will initialise the camera settings object
|
||||
// so we can check if the camera settings have changed, and we need
|
||||
// to reload the decoders.
|
||||
videoStream, _ := capture.GetVideoStream(streams)
|
||||
num, denum := videoStream.(av.VideoCodecData).Framerate()
|
||||
width := videoStream.(av.VideoCodecData).Width()
|
||||
height := videoStream.(av.VideoCodecData).Height()
|
||||
|
||||
if cameraSettings.RTSP != rtspUrl || cameraSettings.SubRTSP != subRtspUrl || cameraSettings.Width != width || cameraSettings.Height != height || cameraSettings.Num != num || cameraSettings.Denum != denum || cameraSettings.Codec != videoStream.(av.VideoCodecData).Type() {
|
||||
|
||||
if cameraSettings.Initialized {
|
||||
decoder.Close()
|
||||
if subStreamEnabled {
|
||||
@@ -187,10 +222,6 @@ func RunAgent(configuration *models.Configuration, communication *models.Communi
|
||||
subQueue.WriteHeader(subStreams)
|
||||
}
|
||||
|
||||
// Configure a MQTT client which helps for a bi-directional communication
|
||||
communication.HandleONVIF = make(chan models.OnvifAction, 1)
|
||||
mqttClient := routers.ConfigureMQTT(configuration, communication)
|
||||
|
||||
// Handle the camera stream
|
||||
go capture.HandleStream(infile, queue, communication)
|
||||
|
||||
@@ -237,11 +268,27 @@ func RunAgent(configuration *models.Configuration, communication *models.Communi
|
||||
// Handle ONVIF actions
|
||||
go onvif.HandleONVIFActions(configuration, communication)
|
||||
|
||||
// If we reach this point, we have a working RTSP connection.
|
||||
communication.CameraConnected = true
|
||||
|
||||
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
// This will go into a blocking state, once this channel is triggered
|
||||
// the agent will cleanup and restart.
|
||||
|
||||
status = <-communication.HandleBootstrap
|
||||
|
||||
// If we reach this point, we are stopping the stream.
|
||||
communication.CameraConnected = false
|
||||
|
||||
// Cancel the main context, this will stop all the other goroutines.
|
||||
(*communication.CancelContext)()
|
||||
|
||||
// We will re open the configuration, might have changed :O!
|
||||
OpenConfig(configDirectory, configuration)
|
||||
|
||||
// We will override the configuration with the environment variables
|
||||
OverrideWithEnvironmentVariables(configuration)
|
||||
|
||||
// Here we are cleaning up everything!
|
||||
if configuration.Config.Offline != "true" {
|
||||
communication.HandleUpload <- "stop"
|
||||
@@ -265,19 +312,9 @@ func RunAgent(configuration *models.Configuration, communication *models.Communi
|
||||
subQueue = nil
|
||||
communication.SubQueue = nil
|
||||
}
|
||||
close(communication.HandleONVIF)
|
||||
communication.HandleONVIF = nil
|
||||
close(communication.HandleLiveHDHandshake)
|
||||
communication.HandleLiveHDHandshake = nil
|
||||
close(communication.HandleMotion)
|
||||
communication.HandleMotion = nil
|
||||
|
||||
// Disconnect MQTT
|
||||
routers.DisconnectMQTT(mqttClient, &configuration.Config)
|
||||
|
||||
// Wait a few seconds to stop the decoder.
|
||||
time.Sleep(time.Second * 3)
|
||||
|
||||
// Waiting for some seconds to make sure everything is properly closed.
|
||||
log.Log.Info("RunAgent: waiting 3 seconds to make sure everything is properly closed.")
|
||||
time.Sleep(time.Second * 3)
|
||||
@@ -303,29 +340,32 @@ func ControlAgent(communication *models.Communication) {
|
||||
var previousPacket int64 = 0
|
||||
var occurence = 0
|
||||
for {
|
||||
packetsR := packageCounter.Load().(int64)
|
||||
if packetsR == previousPacket {
|
||||
// If we are already reconfiguring,
|
||||
// we dont need to check if the stream is blocking.
|
||||
if !communication.IsConfiguring.IsSet() {
|
||||
occurence = occurence + 1
|
||||
|
||||
// If camera is connected, we'll check if we are still receiving packets.
|
||||
if communication.CameraConnected {
|
||||
packetsR := packageCounter.Load().(int64)
|
||||
if packetsR == previousPacket {
|
||||
// If we are already reconfiguring,
|
||||
// we dont need to check if the stream is blocking.
|
||||
if !communication.IsConfiguring.IsSet() {
|
||||
occurence = occurence + 1
|
||||
}
|
||||
} else {
|
||||
occurence = 0
|
||||
}
|
||||
} else {
|
||||
|
||||
occurence = 0
|
||||
log.Log.Info("ControlAgent: Number of packets read " + strconv.FormatInt(packetsR, 10))
|
||||
|
||||
// After 15 seconds without activity this is thrown..
|
||||
if occurence == 3 {
|
||||
log.Log.Info("Main: Restarting machinery.")
|
||||
communication.HandleBootstrap <- "restart"
|
||||
time.Sleep(2 * time.Second)
|
||||
occurence = 0
|
||||
}
|
||||
previousPacket = packageCounter.Load().(int64)
|
||||
}
|
||||
|
||||
log.Log.Info("ControlAgent: Number of packets read " + strconv.FormatInt(packetsR, 10))
|
||||
|
||||
// After 15 seconds without activity this is thrown..
|
||||
if occurence == 3 {
|
||||
log.Log.Info("Main: Restarting machinery.")
|
||||
communication.HandleBootstrap <- "restart"
|
||||
time.Sleep(2 * time.Second)
|
||||
occurence = 0
|
||||
}
|
||||
previousPacket = packageCounter.Load().(int64)
|
||||
|
||||
time.Sleep(5 * time.Second)
|
||||
}
|
||||
}()
|
||||
|
||||
@@ -10,15 +10,12 @@ import (
|
||||
"time"
|
||||
|
||||
mqtt "github.com/eclipse/paho.mqtt.golang"
|
||||
geo "github.com/kellydunn/golang-geo"
|
||||
"github.com/kerberos-io/agent/machinery/src/capture"
|
||||
"github.com/kerberos-io/agent/machinery/src/log"
|
||||
"github.com/kerberos-io/agent/machinery/src/models"
|
||||
"github.com/kerberos-io/joy4/av/pubsub"
|
||||
|
||||
//"github.com/whorfin/go-libjpeg/jpeg"
|
||||
|
||||
geo "github.com/kellydunn/golang-geo"
|
||||
"github.com/kerberos-io/joy4/av"
|
||||
"github.com/kerberos-io/joy4/av/pubsub"
|
||||
"github.com/kerberos-io/joy4/cgo/ffmpeg"
|
||||
)
|
||||
|
||||
@@ -77,18 +74,21 @@ func ProcessMotion(motionCursor *pubsub.QueueCursor, configuration *models.Confi
|
||||
|
||||
// Calculate mask
|
||||
var polyObjects []geo.Polygon
|
||||
for _, polygon := range config.Region.Polygon {
|
||||
coords := polygon.Coordinates
|
||||
poly := geo.Polygon{}
|
||||
for _, c := range coords {
|
||||
x := c.X
|
||||
y := c.Y
|
||||
p := geo.NewPoint(x, y)
|
||||
if !poly.Contains(p) {
|
||||
poly.Add(p)
|
||||
|
||||
if config.Region != nil {
|
||||
for _, polygon := range config.Region.Polygon {
|
||||
coords := polygon.Coordinates
|
||||
poly := geo.Polygon{}
|
||||
for _, c := range coords {
|
||||
x := c.X
|
||||
y := c.Y
|
||||
p := geo.NewPoint(x, y)
|
||||
if !poly.Contains(p) {
|
||||
poly.Add(p)
|
||||
}
|
||||
}
|
||||
polyObjects = append(polyObjects, poly)
|
||||
}
|
||||
polyObjects = append(polyObjects, poly)
|
||||
}
|
||||
|
||||
bounds := img.Bounds()
|
||||
@@ -139,19 +139,21 @@ func ProcessMotion(motionCursor *pubsub.QueueCursor, configuration *models.Confi
|
||||
hour := now.Hour()
|
||||
minute := now.Minute()
|
||||
second := now.Second()
|
||||
timeInterval := config.Timetable[int(weekday)]
|
||||
if timeInterval != nil {
|
||||
start1 := timeInterval.Start1
|
||||
end1 := timeInterval.End1
|
||||
start2 := timeInterval.Start2
|
||||
end2 := timeInterval.End2
|
||||
currentTimeInSeconds := hour*60*60 + minute*60 + second
|
||||
if (currentTimeInSeconds >= start1 && currentTimeInSeconds <= end1) ||
|
||||
(currentTimeInSeconds >= start2 && currentTimeInSeconds <= end2) {
|
||||
if config.Timetable != nil && len(config.Timetable) > 0 {
|
||||
timeInterval := config.Timetable[int(weekday)]
|
||||
if timeInterval != nil {
|
||||
start1 := timeInterval.Start1
|
||||
end1 := timeInterval.End1
|
||||
start2 := timeInterval.Start2
|
||||
end2 := timeInterval.End2
|
||||
currentTimeInSeconds := hour*60*60 + minute*60 + second
|
||||
if (currentTimeInSeconds >= start1 && currentTimeInSeconds <= end1) ||
|
||||
(currentTimeInSeconds >= start2 && currentTimeInSeconds <= end2) {
|
||||
|
||||
} else {
|
||||
detectMotion = false
|
||||
log.Log.Info("ProcessMotion: Time interval not valid, disabling motion detection.")
|
||||
} else {
|
||||
detectMotion = false
|
||||
log.Log.Info("ProcessMotion: Time interval not valid, disabling motion detection.")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -163,7 +165,7 @@ func ProcessMotion(motionCursor *pubsub.QueueCursor, configuration *models.Confi
|
||||
if detectMotion && isPixelChangeThresholdReached {
|
||||
|
||||
// If offline mode is disabled, send a message to the hub
|
||||
if config.Offline == "false" {
|
||||
if config.Offline != "true" {
|
||||
if mqttClient != nil {
|
||||
if key != "" {
|
||||
mqttClient.Publish("kerberos/"+key+"/device/"+config.Key+"/motion", 2, false, "motion")
|
||||
|
||||
@@ -1,46 +1,55 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/kerberos-io/agent/machinery/src/log"
|
||||
"gopkg.in/mgo.v2"
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
)
|
||||
|
||||
type DB struct {
|
||||
Session *mgo.Session
|
||||
Client *mongo.Client
|
||||
}
|
||||
|
||||
var _init_ctx sync.Once
|
||||
var _instance *DB
|
||||
var DatabaseName = "KerberosFactory"
|
||||
|
||||
func New() *mgo.Session {
|
||||
func New() *mongo.Client {
|
||||
|
||||
host := os.Getenv("MONGODB_HOST")
|
||||
database := os.Getenv("MONGODB_DATABASE_CREDENTIALS")
|
||||
databaseCredentials := os.Getenv("MONGODB_DATABASE_CREDENTIALS")
|
||||
replicaset := os.Getenv("MONGODB_REPLICASET")
|
||||
username := os.Getenv("MONGODB_USERNAME")
|
||||
password := os.Getenv("MONGODB_PASSWORD")
|
||||
authentication := "SCRAM-SHA-256"
|
||||
|
||||
_init_ctx.Do(func() {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
_instance = new(DB)
|
||||
mongoDBDialInfo := &mgo.DialInfo{
|
||||
Addrs: strings.Split(host, ","),
|
||||
Timeout: 3 * time.Second,
|
||||
Database: database,
|
||||
Username: username,
|
||||
Password: password,
|
||||
mongodbURI := fmt.Sprintf("mongodb://%s:%s@%s", username, password, host)
|
||||
if replicaset != "" {
|
||||
mongodbURI = fmt.Sprintf("%s/?replicaSet=%s", mongodbURI, replicaset)
|
||||
}
|
||||
session, err := mgo.DialWithInfo(mongoDBDialInfo)
|
||||
|
||||
client, err := mongo.Connect(ctx, options.Client().ApplyURI(mongodbURI).SetAuth(options.Credential{
|
||||
AuthMechanism: authentication,
|
||||
AuthSource: databaseCredentials,
|
||||
Username: username,
|
||||
Password: password,
|
||||
}))
|
||||
if err != nil {
|
||||
log.Log.Error(fmt.Sprintf("Failed to connect to database: %s", err.Error()))
|
||||
fmt.Printf("Error setting up mongodb connection: %+v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
_instance.Session = session
|
||||
_instance.Client = client
|
||||
})
|
||||
|
||||
return _instance.Session
|
||||
return _instance.Client
|
||||
}
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
package models
|
||||
|
||||
type APIResponse struct {
|
||||
Data interface{} `json:"data" bson:"data"`
|
||||
Message interface{} `json:"message" bson:"message"`
|
||||
Data interface{} `json:"data" bson:"data"`
|
||||
Message interface{} `json:"message" bson:"message"`
|
||||
PTZFunctions interface{} `json:"ptz_functions" bson:"ptz_functions"`
|
||||
CanZoom bool `json:"can_zoom" bson:"can_zoom"`
|
||||
CanPanTilt bool `json:"can_pan_tilt" bson:"can_pan_tilt"`
|
||||
}
|
||||
|
||||
type OnvifCredentials struct {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
||||
@@ -12,6 +13,8 @@ import (
|
||||
// The communication struct that is managing
|
||||
// all the communication between the different goroutines.
|
||||
type Communication struct {
|
||||
Context *context.Context
|
||||
CancelContext *context.CancelFunc
|
||||
PackageCounter *atomic.Value
|
||||
LastPacketTimer *atomic.Value
|
||||
CloudTimestamp *atomic.Value
|
||||
|
||||
@@ -73,7 +73,7 @@ type IPCamera struct {
|
||||
RTSP string `json:"rtsp"`
|
||||
SubRTSP string `json:"sub_rtsp"`
|
||||
FPS string `json:"fps"`
|
||||
ONVIF bool `json:"onvif,omitempty" bson:"onvif"`
|
||||
ONVIF string `json:"onvif,omitempty" bson:"onvif"`
|
||||
ONVIFXAddr string `json:"onvif_xaddr,omitempty" bson:"onvif_xaddr"`
|
||||
ONVIFUsername string `json:"onvif_username,omitempty" bson:"onvif_username"`
|
||||
ONVIFPassword string `json:"onvif_password,omitempty" bson:"onvif_password"`
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/kerberos-io/agent/machinery/src/log"
|
||||
"github.com/kerberos-io/agent/machinery/src/models"
|
||||
"github.com/kerberos-io/onvif/media"
|
||||
@@ -31,7 +32,8 @@ func HandleONVIFActions(configuration *models.Configuration, communication *mode
|
||||
json.Unmarshal(b, &ptzAction)
|
||||
|
||||
// Connect to Onvif device
|
||||
device, err := ConnectToOnvifDevice(configuration)
|
||||
cameraConfiguration := configuration.Config.Capture.IPCamera
|
||||
device, err := ConnectToOnvifDevice(&cameraConfiguration)
|
||||
if err == nil {
|
||||
|
||||
// Get token from the first profile
|
||||
@@ -39,7 +41,7 @@ func HandleONVIFActions(configuration *models.Configuration, communication *mode
|
||||
if err == nil {
|
||||
|
||||
// Get the configurations from the device
|
||||
configurations, err := GetConfigurationsFromDevice(device)
|
||||
configurations, err := GetPTZConfigurationsFromDevice(device)
|
||||
|
||||
if err == nil {
|
||||
|
||||
@@ -100,16 +102,13 @@ func HandleONVIFActions(configuration *models.Configuration, communication *mode
|
||||
log.Log.Debug("HandleONVIFActions: finished")
|
||||
}
|
||||
|
||||
func ConnectToOnvifDevice(configuration *models.Configuration) (*onvif.Device, error) {
|
||||
func ConnectToOnvifDevice(cameraConfiguration *models.IPCamera) (*onvif.Device, error) {
|
||||
log.Log.Debug("ConnectToOnvifDevice: started")
|
||||
|
||||
config := configuration.Config
|
||||
|
||||
// Get the capabilities of the ONVIF device
|
||||
device, err := onvif.NewDevice(onvif.DeviceParams{
|
||||
Xaddr: config.Capture.IPCamera.ONVIFXAddr,
|
||||
Username: config.Capture.IPCamera.ONVIFUsername,
|
||||
Password: config.Capture.IPCamera.ONVIFPassword,
|
||||
Xaddr: cameraConfiguration.ONVIFXAddr,
|
||||
Username: cameraConfiguration.ONVIFUsername,
|
||||
Password: cameraConfiguration.ONVIFPassword,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
@@ -154,11 +153,11 @@ func GetTokenFromProfile(device *onvif.Device, profileId int) (xsd.ReferenceToke
|
||||
return profileToken, err
|
||||
}
|
||||
|
||||
func GetConfigurationsFromDevice(device *onvif.Device) (ptz.GetConfigurationsResponse, error) {
|
||||
func GetPTZConfigurationsFromDevice(device *onvif.Device) (ptz.GetConfigurationsResponse, error) {
|
||||
// We'll try to receive the PTZ configurations from the server
|
||||
var configurations ptz.GetConfigurationsResponse
|
||||
|
||||
// Get the configurations from the device
|
||||
// Get the PTZ configurations from the device
|
||||
resp, err := device.CallMethod(ptz.GetConfigurations{})
|
||||
if err == nil {
|
||||
defer resp.Body.Close()
|
||||
@@ -167,11 +166,11 @@ func GetConfigurationsFromDevice(device *onvif.Device) (ptz.GetConfigurationsRes
|
||||
stringBody := string(b)
|
||||
decodedXML, et, err := getXMLNode(stringBody, "GetConfigurationsResponse")
|
||||
if err != nil {
|
||||
log.Log.Error("GetConfigurationsFromDevice: " + err.Error())
|
||||
log.Log.Error("GetPTZConfigurationsFromDevice: " + err.Error())
|
||||
return configurations, err
|
||||
} else {
|
||||
if err := decodedXML.DecodeElement(&configurations, et); err != nil {
|
||||
log.Log.Error("GetConfigurationsFromDevice: " + err.Error())
|
||||
log.Log.Error("GetPTZConfigurationsFromDevice: " + err.Error())
|
||||
return configurations, err
|
||||
}
|
||||
}
|
||||
@@ -317,3 +316,89 @@ func getXMLNode(xmlBody string, nodeName string) (*xml.Decoder, *xml.StartElemen
|
||||
}
|
||||
return nil, nil, errors.New("error in NodeName - username and password might be wrong")
|
||||
}
|
||||
|
||||
func GetPTZFunctionsFromDevice(configurations ptz.GetConfigurationsResponse) ([]string, bool, bool) {
|
||||
var functions []string
|
||||
canZoom := false
|
||||
canPanTilt := false
|
||||
|
||||
if configurations.PTZConfiguration.DefaultAbsolutePantTiltPositionSpace != "" {
|
||||
functions = append(functions, "AbsolutePanTiltMove")
|
||||
canPanTilt = true
|
||||
}
|
||||
if configurations.PTZConfiguration.DefaultAbsoluteZoomPositionSpace != "" {
|
||||
functions = append(functions, "AbsoluteZoomMove")
|
||||
canZoom = true
|
||||
}
|
||||
if configurations.PTZConfiguration.DefaultRelativePanTiltTranslationSpace != "" {
|
||||
functions = append(functions, "RelativePanTiltMove")
|
||||
canPanTilt = true
|
||||
}
|
||||
if configurations.PTZConfiguration.DefaultRelativeZoomTranslationSpace != "" {
|
||||
functions = append(functions, "RelativeZoomMove")
|
||||
canZoom = true
|
||||
}
|
||||
if configurations.PTZConfiguration.DefaultContinuousPanTiltVelocitySpace != "" {
|
||||
functions = append(functions, "ContinuousPanTiltMove")
|
||||
canPanTilt = true
|
||||
}
|
||||
if configurations.PTZConfiguration.DefaultContinuousZoomVelocitySpace != "" {
|
||||
functions = append(functions, "ContinuousZoomMove")
|
||||
canZoom = true
|
||||
}
|
||||
if configurations.PTZConfiguration.DefaultPTZSpeed != nil {
|
||||
functions = append(functions, "PTZSpeed")
|
||||
}
|
||||
if configurations.PTZConfiguration.DefaultPTZTimeout != "" {
|
||||
functions = append(functions, "PTZTimeout")
|
||||
}
|
||||
|
||||
return functions, canZoom, canPanTilt
|
||||
}
|
||||
|
||||
// VerifyOnvifConnection godoc
|
||||
// @Router /api/onvif/verify [post]
|
||||
// @ID verify-onvif
|
||||
// @Security Bearer
|
||||
// @securityDefinitions.apikey Bearer
|
||||
// @in header
|
||||
// @name Authorization
|
||||
// @Tags config
|
||||
// @Param cameraConfig body models.IPCamera true "Camera Config"
|
||||
// @Summary Will verify the ONVIF connectivity.
|
||||
// @Description Will verify the ONVIF connectivity.
|
||||
// @Success 200 {object} models.APIResponse
|
||||
func VerifyOnvifConnection(c *gin.Context) {
|
||||
var cameraConfig models.IPCamera
|
||||
err := c.BindJSON(&cameraConfig)
|
||||
if err == nil {
|
||||
device, err := ConnectToOnvifDevice(&cameraConfig)
|
||||
if err == nil {
|
||||
// Get the list of configurations
|
||||
configurations, err := GetPTZConfigurationsFromDevice(device)
|
||||
if err == nil {
|
||||
|
||||
// Check if can zoom and/or pan/tilt is supported
|
||||
ptzFunctions, canZoom, canPanTilt := GetPTZFunctionsFromDevice(configurations)
|
||||
c.JSON(200, models.APIResponse{
|
||||
Data: device,
|
||||
PTZFunctions: ptzFunctions,
|
||||
CanZoom: canZoom,
|
||||
CanPanTilt: canPanTilt,
|
||||
})
|
||||
} else {
|
||||
c.JSON(400, models.APIResponse{
|
||||
Message: "Something went wrong while getting the configurations " + err.Error(),
|
||||
})
|
||||
}
|
||||
} else {
|
||||
c.JSON(400, models.APIResponse{
|
||||
Message: "Something went wrong while verifying the ONVIF connection " + err.Error(),
|
||||
})
|
||||
}
|
||||
} else {
|
||||
c.JSON(400, models.APIResponse{
|
||||
Message: "Something went wrong while receiving the config " + err.Error(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,7 +42,8 @@ func LoginToOnvif(c *gin.Context) {
|
||||
},
|
||||
}
|
||||
|
||||
device, err := onvif.ConnectToOnvifDevice(configuration)
|
||||
cameraConfiguration := configuration.Config.Capture.IPCamera
|
||||
device, err := onvif.ConnectToOnvifDevice(&cameraConfiguration)
|
||||
if err == nil {
|
||||
c.JSON(200, gin.H{
|
||||
"device": device,
|
||||
@@ -85,7 +86,8 @@ func GetOnvifCapabilities(c *gin.Context) {
|
||||
},
|
||||
}
|
||||
|
||||
device, err := onvif.ConnectToOnvifDevice(configuration)
|
||||
cameraConfiguration := configuration.Config.Capture.IPCamera
|
||||
device, err := onvif.ConnectToOnvifDevice(&cameraConfiguration)
|
||||
if err == nil {
|
||||
c.JSON(200, gin.H{
|
||||
"capabilities": onvif.GetCapabilitiesFromDevice(device),
|
||||
@@ -128,7 +130,8 @@ func DoOnvifPanTilt(c *gin.Context) {
|
||||
},
|
||||
}
|
||||
|
||||
device, err := onvif.ConnectToOnvifDevice(configuration)
|
||||
cameraConfiguration := configuration.Config.Capture.IPCamera
|
||||
device, err := onvif.ConnectToOnvifDevice(&cameraConfiguration)
|
||||
|
||||
if err == nil {
|
||||
// Get token from the first profile
|
||||
@@ -137,13 +140,13 @@ func DoOnvifPanTilt(c *gin.Context) {
|
||||
if err == nil {
|
||||
|
||||
// Get the configurations from the device
|
||||
configurations, err := onvif.GetConfigurationsFromDevice(device)
|
||||
ptzConfigurations, err := onvif.GetPTZConfigurationsFromDevice(device)
|
||||
|
||||
if err == nil {
|
||||
|
||||
pan := onvifPanTilt.Pan
|
||||
tilt := onvifPanTilt.Tilt
|
||||
err := onvif.ContinuousPanTilt(device, configurations, token, pan, tilt)
|
||||
err := onvif.ContinuousPanTilt(device, ptzConfigurations, token, pan, tilt)
|
||||
if err == nil {
|
||||
c.JSON(200, models.APIResponse{
|
||||
Message: "Successfully pan/tilted the camera",
|
||||
@@ -201,7 +204,8 @@ func DoOnvifZoom(c *gin.Context) {
|
||||
},
|
||||
}
|
||||
|
||||
device, err := onvif.ConnectToOnvifDevice(configuration)
|
||||
cameraConfiguration := configuration.Config.Capture.IPCamera
|
||||
device, err := onvif.ConnectToOnvifDevice(&cameraConfiguration)
|
||||
|
||||
if err == nil {
|
||||
// Get token from the first profile
|
||||
@@ -209,13 +213,13 @@ func DoOnvifZoom(c *gin.Context) {
|
||||
|
||||
if err == nil {
|
||||
|
||||
// Get the configurations from the device
|
||||
configurations, err := onvif.GetConfigurationsFromDevice(device)
|
||||
// Get the PTZ configurations from the device
|
||||
ptzConfigurations, err := onvif.GetPTZConfigurationsFromDevice(device)
|
||||
|
||||
if err == nil {
|
||||
|
||||
zoom := onvifZoom.Zoom
|
||||
err := onvif.ContinuousZoom(device, configurations, token, zoom)
|
||||
err := onvif.ContinuousZoom(device, ptzConfigurations, token, zoom)
|
||||
if err == nil {
|
||||
c.JSON(200, models.APIResponse{
|
||||
Message: "Successfully zoomed the camera",
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
jwt "github.com/appleboy/gin-jwt/v2"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/kerberos-io/agent/machinery/src/capture"
|
||||
"github.com/kerberos-io/agent/machinery/src/onvif"
|
||||
"github.com/kerberos-io/agent/machinery/src/routers/websocket"
|
||||
|
||||
"github.com/kerberos-io/agent/machinery/src/cloud"
|
||||
@@ -16,7 +17,7 @@ import (
|
||||
"github.com/kerberos-io/agent/machinery/src/utils"
|
||||
)
|
||||
|
||||
func AddRoutes(r *gin.Engine, authMiddleware *jwt.GinJWTMiddleware, configuration *models.Configuration, communication *models.Communication) *gin.RouterGroup {
|
||||
func AddRoutes(r *gin.Engine, authMiddleware *jwt.GinJWTMiddleware, configDirectory string, configuration *models.Configuration, communication *models.Communication) *gin.RouterGroup {
|
||||
|
||||
r.GET("/ws", func(c *gin.Context) {
|
||||
websocket.WebsocketHandler(c, communication)
|
||||
@@ -39,7 +40,7 @@ func AddRoutes(r *gin.Engine, authMiddleware *jwt.GinJWTMiddleware, configuratio
|
||||
var config models.Config
|
||||
err := c.BindJSON(&config)
|
||||
if err == nil {
|
||||
err := components.SaveConfig(config, configuration, communication)
|
||||
err := components.SaveConfig(configDirectory, config, configuration, communication)
|
||||
if err == nil {
|
||||
c.JSON(200, gin.H{
|
||||
"data": "☄ Reconfiguring",
|
||||
@@ -62,19 +63,18 @@ func AddRoutes(r *gin.Engine, authMiddleware *jwt.GinJWTMiddleware, configuratio
|
||||
|
||||
api.GET("/dashboard", func(c *gin.Context) {
|
||||
|
||||
// This will return the timestamp when the last packet was correctyl received
|
||||
// this is to calculate if the camera connection is still working.
|
||||
lastPacketReceived := int64(0)
|
||||
if communication.LastPacketTimer != nil && communication.LastPacketTimer.Load() != nil {
|
||||
lastPacketReceived = communication.LastPacketTimer.Load().(int64)
|
||||
}
|
||||
// Check if camera is online.
|
||||
cameraIsOnline := communication.CameraConnected
|
||||
|
||||
// If an agent is properly setup with Kerberos Hub, we will send
|
||||
// a ping to Kerberos Hub every 15seconds. On receiving a positive response
|
||||
// it will update the CloudTimestamp value.
|
||||
cloudTimestamp := int64(0)
|
||||
cloudIsOnline := false
|
||||
if communication.CloudTimestamp != nil && communication.CloudTimestamp.Load() != nil {
|
||||
cloudTimestamp = communication.CloudTimestamp.Load().(int64)
|
||||
timestamp := communication.CloudTimestamp.Load().(int64)
|
||||
if timestamp > 0 {
|
||||
cloudIsOnline = true
|
||||
}
|
||||
}
|
||||
|
||||
// The total number of recordings stored in the directory.
|
||||
@@ -99,8 +99,8 @@ func AddRoutes(r *gin.Engine, authMiddleware *jwt.GinJWTMiddleware, configuratio
|
||||
|
||||
c.JSON(200, gin.H{
|
||||
"offlineMode": configuration.Config.Offline,
|
||||
"cameraOnline": lastPacketReceived,
|
||||
"cloudOnline": cloudTimestamp,
|
||||
"cameraOnline": cameraIsOnline,
|
||||
"cloudOnline": cloudIsOnline,
|
||||
"numberOfRecordings": numberOfRecordings,
|
||||
"days": days,
|
||||
"latestEvents": latestEvents,
|
||||
@@ -165,7 +165,7 @@ func AddRoutes(r *gin.Engine, authMiddleware *jwt.GinJWTMiddleware, configuratio
|
||||
var config models.Config
|
||||
err := c.BindJSON(&config)
|
||||
if err == nil {
|
||||
err := components.SaveConfig(config, configuration, communication)
|
||||
err := components.SaveConfig(configDirectory, config, configuration, communication)
|
||||
if err == nil {
|
||||
c.JSON(200, gin.H{
|
||||
"data": "☄ Reconfiguring",
|
||||
@@ -196,6 +196,10 @@ func AddRoutes(r *gin.Engine, authMiddleware *jwt.GinJWTMiddleware, configuratio
|
||||
})
|
||||
})
|
||||
|
||||
api.POST("/onvif/verify", func(c *gin.Context) {
|
||||
onvif.VerifyOnvifConnection(c)
|
||||
})
|
||||
|
||||
api.POST("/hub/verify", func(c *gin.Context) {
|
||||
cloud.VerifyHub(c)
|
||||
})
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package http
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
jwt "github.com/appleboy/gin-jwt/v2"
|
||||
"github.com/gin-contrib/pprof"
|
||||
"github.com/gin-gonic/contrib/static"
|
||||
@@ -33,7 +35,7 @@ import (
|
||||
// @in header
|
||||
// @name Authorization
|
||||
|
||||
func StartServer(configuration *models.Configuration, communication *models.Communication) {
|
||||
func StartServer(configDirectory string, configuration *models.Configuration, communication *models.Communication) {
|
||||
|
||||
// Initialize REST API
|
||||
r := gin.Default()
|
||||
@@ -55,14 +57,25 @@ func StartServer(configuration *models.Configuration, communication *models.Comm
|
||||
}
|
||||
|
||||
// Add all routes
|
||||
AddRoutes(r, authMiddleware, configuration, communication)
|
||||
AddRoutes(r, authMiddleware, configDirectory, configuration, communication)
|
||||
|
||||
// Update environment variables
|
||||
environmentVariables := configDirectory + "/www/env.js"
|
||||
if os.Getenv("AGENT_MODE") == "demo" {
|
||||
demoEnvironmentVariables := configDirectory + "/www/env.demo.js"
|
||||
// Move demo environment variables to environment variables
|
||||
err := os.Rename(demoEnvironmentVariables, environmentVariables)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// Add static routes to UI
|
||||
r.Use(static.Serve("/", static.LocalFile("./www", true)))
|
||||
r.Use(static.Serve("/dashboard", 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("/login", static.LocalFile("./www", true)))
|
||||
r.Use(static.Serve("/", static.LocalFile(configDirectory+"/www", true)))
|
||||
r.Use(static.Serve("/dashboard", static.LocalFile(configDirectory+"/www", true)))
|
||||
r.Use(static.Serve("/media", static.LocalFile(configDirectory+"/www", true)))
|
||||
r.Use(static.Serve("/settings", static.LocalFile(configDirectory+"/www", true)))
|
||||
r.Use(static.Serve("/login", static.LocalFile(configDirectory+"/www", true)))
|
||||
r.Handle("GET", "/file/*filepath", Files)
|
||||
|
||||
// Run the api on port
|
||||
|
||||
@@ -5,6 +5,6 @@ import (
|
||||
"github.com/kerberos-io/agent/machinery/src/routers/http"
|
||||
)
|
||||
|
||||
func StartWebserver(configuration *models.Configuration, communication *models.Communication) {
|
||||
http.StartServer(configuration, communication)
|
||||
func StartWebserver(configDirectory string, configuration *models.Configuration, communication *models.Communication) {
|
||||
http.StartServer(configDirectory, configuration, communication)
|
||||
}
|
||||
|
||||
@@ -13,10 +13,38 @@ import (
|
||||
"github.com/kerberos-io/agent/machinery/src/webrtc"
|
||||
)
|
||||
|
||||
// We'll cache the MQTT settings to know if we need to reinitialize the MQTT client connection.
|
||||
// If we update the configuration but no new MQTT settings are provided, we don't need to restart it.
|
||||
var PREV_MQTTURI string
|
||||
var PREV_MQTTUsername string
|
||||
var PREV_MQTTPassword string
|
||||
var PREV_HubKey string
|
||||
var PREV_AgentKey string
|
||||
|
||||
func HasMQTTClientModified(configuration *models.Configuration) bool {
|
||||
MTTURI := configuration.Config.MQTTURI
|
||||
MTTUsername := configuration.Config.MQTTUsername
|
||||
MQTTPassword := configuration.Config.MQTTPassword
|
||||
HubKey := configuration.Config.HubKey
|
||||
AgentKey := configuration.Config.Key
|
||||
if PREV_MQTTURI != MTTURI || PREV_MQTTUsername != MTTUsername || PREV_MQTTPassword != MQTTPassword || PREV_HubKey != HubKey || PREV_AgentKey != AgentKey {
|
||||
log.Log.Info("HasMQTTClientModified: MQTT settings have been modified, restarting MQTT client.")
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func ConfigureMQTT(configuration *models.Configuration, communication *models.Communication) mqtt.Client {
|
||||
|
||||
config := configuration.Config
|
||||
|
||||
// Set the MQTT settings.
|
||||
PREV_MQTTURI = configuration.Config.MQTTURI
|
||||
PREV_MQTTUsername = configuration.Config.MQTTUsername
|
||||
PREV_MQTTPassword = configuration.Config.MQTTPassword
|
||||
PREV_HubKey = configuration.Config.HubKey
|
||||
PREV_AgentKey = configuration.Config.Key
|
||||
|
||||
if config.Offline == "true" {
|
||||
log.Log.Info("ConfigureMQTT: not starting as running in Offline mode.")
|
||||
} else {
|
||||
@@ -78,7 +106,6 @@ func ConfigureMQTT(configuration *models.Configuration, communication *models.Co
|
||||
webrtc.CandidateArrays = make(map[string](chan string))
|
||||
|
||||
opts.OnConnect = func(c mqtt.Client) {
|
||||
|
||||
// We managed to connect to the MQTT broker, hurray!
|
||||
log.Log.Info("ConfigureMQTT: " + mqttClientID + " connected to " + mqttURL)
|
||||
|
||||
@@ -117,11 +144,15 @@ func MQTTListenerHandleLiveSD(mqttClient mqtt.Client, hubKey string, configurati
|
||||
config := configuration.Config
|
||||
topicRequest := "kerberos/" + hubKey + "/device/" + config.Key + "/request-live"
|
||||
mqttClient.Subscribe(topicRequest, 0, func(c mqtt.Client, msg mqtt.Message) {
|
||||
select {
|
||||
case communication.HandleLiveSD <- time.Now().Unix():
|
||||
default:
|
||||
if communication.CameraConnected {
|
||||
select {
|
||||
case communication.HandleLiveSD <- time.Now().Unix():
|
||||
default:
|
||||
}
|
||||
log.Log.Info("MQTTListenerHandleLiveSD: received request to livestream.")
|
||||
} else {
|
||||
log.Log.Info("MQTTListenerHandleLiveSD: received request to livestream, but camera is not connected.")
|
||||
}
|
||||
log.Log.Info("MQTTListenerHandleLiveSD: received request to livestream.")
|
||||
msg.Ack()
|
||||
})
|
||||
}
|
||||
@@ -130,12 +161,16 @@ func MQTTListenerHandleLiveHDHandshake(mqttClient mqtt.Client, hubKey string, co
|
||||
config := configuration.Config
|
||||
topicRequestWebRtc := config.Key + "/register"
|
||||
mqttClient.Subscribe(topicRequestWebRtc, 0, func(c mqtt.Client, msg mqtt.Message) {
|
||||
log.Log.Info("MQTTListenerHandleLiveHDHandshake: received request to setup webrtc.")
|
||||
var sdp models.SDPPayload
|
||||
json.Unmarshal(msg.Payload(), &sdp)
|
||||
select {
|
||||
case communication.HandleLiveHDHandshake <- sdp:
|
||||
default:
|
||||
if communication.CameraConnected {
|
||||
var sdp models.SDPPayload
|
||||
json.Unmarshal(msg.Payload(), &sdp)
|
||||
select {
|
||||
case communication.HandleLiveHDHandshake <- sdp:
|
||||
default:
|
||||
}
|
||||
log.Log.Info("MQTTListenerHandleLiveHDHandshake: received request to setup webrtc.")
|
||||
} else {
|
||||
log.Log.Info("MQTTListenerHandleLiveHDHandshake: received request to setup webrtc, but camera is not connected.")
|
||||
}
|
||||
msg.Ack()
|
||||
})
|
||||
@@ -145,9 +180,13 @@ func MQTTListenerHandleLiveHDKeepalive(mqttClient mqtt.Client, hubKey string, co
|
||||
config := configuration.Config
|
||||
topicKeepAlive := fmt.Sprintf("kerberos/webrtc/keepalivehub/%s", config.Key)
|
||||
mqttClient.Subscribe(topicKeepAlive, 0, func(c mqtt.Client, msg mqtt.Message) {
|
||||
alive := string(msg.Payload())
|
||||
communication.HandleLiveHDKeepalive <- alive
|
||||
log.Log.Info("MQTTListenerHandleLiveHDKeepalive: Received keepalive: " + alive)
|
||||
if communication.CameraConnected {
|
||||
alive := string(msg.Payload())
|
||||
communication.HandleLiveHDKeepalive <- alive
|
||||
log.Log.Info("MQTTListenerHandleLiveHDKeepalive: Received keepalive: " + alive)
|
||||
} else {
|
||||
log.Log.Info("MQTTListenerHandleLiveHDKeepalive: received keepalive, but camera is not connected.")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -155,9 +194,13 @@ func MQTTListenerHandleLiveHDPeers(mqttClient mqtt.Client, hubKey string, config
|
||||
config := configuration.Config
|
||||
topicPeers := fmt.Sprintf("kerberos/webrtc/peers/%s", config.Key)
|
||||
mqttClient.Subscribe(topicPeers, 0, func(c mqtt.Client, msg mqtt.Message) {
|
||||
peerCount := string(msg.Payload())
|
||||
communication.HandleLiveHDPeers <- peerCount
|
||||
log.Log.Info("MQTTListenerHandleLiveHDPeers: Number of peers listening: " + peerCount)
|
||||
if communication.CameraConnected {
|
||||
peerCount := string(msg.Payload())
|
||||
communication.HandleLiveHDPeers <- peerCount
|
||||
log.Log.Info("MQTTListenerHandleLiveHDPeers: Number of peers listening: " + peerCount)
|
||||
} else {
|
||||
log.Log.Info("MQTTListenerHandleLiveHDPeers: received peer count, but camera is not connected.")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -165,19 +208,23 @@ func MQTTListenerHandleLiveHDCandidates(mqttClient mqtt.Client, hubKey string, c
|
||||
config := configuration.Config
|
||||
topicCandidates := "candidate/cloud"
|
||||
mqttClient.Subscribe(topicCandidates, 0, func(c mqtt.Client, msg mqtt.Message) {
|
||||
var candidate models.Candidate
|
||||
json.Unmarshal(msg.Payload(), &candidate)
|
||||
if candidate.CloudKey == config.Key {
|
||||
key := candidate.CloudKey + "/" + candidate.Cuuid
|
||||
candidatesExists := false
|
||||
var channel chan string
|
||||
for !candidatesExists {
|
||||
webrtc.CandidatesMutex.Lock()
|
||||
channel, candidatesExists = webrtc.CandidateArrays[key]
|
||||
webrtc.CandidatesMutex.Unlock()
|
||||
if communication.CameraConnected {
|
||||
var candidate models.Candidate
|
||||
json.Unmarshal(msg.Payload(), &candidate)
|
||||
if candidate.CloudKey == config.Key {
|
||||
key := candidate.CloudKey + "/" + candidate.Cuuid
|
||||
candidatesExists := false
|
||||
var channel chan string
|
||||
for !candidatesExists {
|
||||
webrtc.CandidatesMutex.Lock()
|
||||
channel, candidatesExists = webrtc.CandidateArrays[key]
|
||||
webrtc.CandidatesMutex.Unlock()
|
||||
}
|
||||
log.Log.Info("MQTTListenerHandleLiveHDCandidates: " + string(msg.Payload()))
|
||||
channel <- string(msg.Payload())
|
||||
}
|
||||
log.Log.Info("MQTTListenerHandleLiveHDCandidates: " + string(msg.Payload()))
|
||||
channel <- string(msg.Payload())
|
||||
} else {
|
||||
log.Log.Info("MQTTListenerHandleLiveHDCandidates: received candidate, but camera is not connected.")
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -186,23 +233,28 @@ func MQTTListenerHandleONVIF(mqttClient mqtt.Client, hubKey string, configuratio
|
||||
config := configuration.Config
|
||||
topicOnvif := fmt.Sprintf("kerberos/onvif/%s", config.Key)
|
||||
mqttClient.Subscribe(topicOnvif, 0, func(c mqtt.Client, msg mqtt.Message) {
|
||||
var onvifAction models.OnvifAction
|
||||
json.Unmarshal(msg.Payload(), &onvifAction)
|
||||
communication.HandleONVIF <- onvifAction
|
||||
log.Log.Info("MQTTListenerHandleONVIF: Received an action - " + onvifAction.Action)
|
||||
if communication.CameraConnected {
|
||||
var onvifAction models.OnvifAction
|
||||
json.Unmarshal(msg.Payload(), &onvifAction)
|
||||
communication.HandleONVIF <- onvifAction
|
||||
log.Log.Info("MQTTListenerHandleONVIF: Received an action - " + onvifAction.Action)
|
||||
} else {
|
||||
log.Log.Info("MQTTListenerHandleONVIF: received action, but camera is not connected.")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func DisconnectMQTT(mqttClient mqtt.Client, config *models.Config) {
|
||||
if mqttClient != nil {
|
||||
// Cleanup all subscriptions.
|
||||
mqttClient.Unsubscribe("kerberos/" + config.HubKey + "/device/" + config.Key + "/request-live")
|
||||
mqttClient.Unsubscribe(config.Key + "/register")
|
||||
mqttClient.Unsubscribe("kerberos/webrtc/keepalivehub/" + config.Key)
|
||||
mqttClient.Unsubscribe("kerberos/webrtc/peers/" + config.Key)
|
||||
// Cleanup all subscriptions
|
||||
mqttClient.Unsubscribe("kerberos/" + PREV_HubKey + "/device/" + PREV_AgentKey + "/request-live")
|
||||
mqttClient.Unsubscribe(PREV_AgentKey + "/register")
|
||||
mqttClient.Unsubscribe("kerberos/webrtc/keepalivehub/" + PREV_AgentKey)
|
||||
mqttClient.Unsubscribe("kerberos/webrtc/peers/" + PREV_AgentKey)
|
||||
mqttClient.Unsubscribe("candidate/cloud")
|
||||
mqttClient.Unsubscribe("kerberos/onvif/" + config.Key)
|
||||
mqttClient.Unsubscribe("kerberos/onvif/" + PREV_AgentKey)
|
||||
mqttClient.Disconnect(1000)
|
||||
mqttClient = nil
|
||||
log.Log.Info("DisconnectMQTT: MQTT client disconnected.")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,6 +51,7 @@ func WebsocketHandler(c *gin.Context, communication *models.Communication) {
|
||||
w := c.Writer
|
||||
r := c.Request
|
||||
conn, err := upgrader.Upgrade(w, r, nil)
|
||||
|
||||
// error handling here
|
||||
if err == nil {
|
||||
defer conn.Close()
|
||||
@@ -83,28 +84,29 @@ func WebsocketHandler(c *gin.Context, communication *models.Communication) {
|
||||
_, exists := sockets[clientID].Cancels["stream-sd"]
|
||||
if exists {
|
||||
sockets[clientID].Cancels["stream-sd"]()
|
||||
delete(sockets[clientID].Cancels, "stream-sd")
|
||||
} else {
|
||||
log.Log.Error("Streaming sd does not exists for " + clientID)
|
||||
}
|
||||
|
||||
case "stream-sd":
|
||||
startStrean := Message{
|
||||
ClientID: clientID,
|
||||
MessageType: "stream-sd",
|
||||
Message: map[string]string{
|
||||
"message": "Start streaming low resolution",
|
||||
},
|
||||
}
|
||||
sockets[clientID].WriteJson(startStrean)
|
||||
if communication.CameraConnected {
|
||||
_, exists := sockets[clientID].Cancels["stream-sd"]
|
||||
if exists {
|
||||
log.Log.Info("Already streaming sd for " + clientID)
|
||||
} else {
|
||||
startStream := Message{
|
||||
ClientID: clientID,
|
||||
MessageType: "stream-sd",
|
||||
Message: map[string]string{
|
||||
"message": "Start streaming low resolution",
|
||||
},
|
||||
}
|
||||
sockets[clientID].WriteJson(startStream)
|
||||
|
||||
_, exists := sockets[clientID].Cancels["stream-sd"]
|
||||
if exists {
|
||||
log.Log.Info("Already streaming sd for " + clientID)
|
||||
} else {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
sockets[clientID].Cancels["stream-sd"] = cancel
|
||||
go ForwardSDStream(ctx, clientID, sockets[clientID], communication)
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
sockets[clientID].Cancels["stream-sd"] = cancel
|
||||
go ForwardSDStream(ctx, clientID, sockets[clientID], communication)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -173,5 +175,14 @@ logreader:
|
||||
|
||||
frame.Free()
|
||||
|
||||
// Close socket for streaming
|
||||
_, exists := connection.Cancels["stream-sd"]
|
||||
if exists {
|
||||
delete(connection.Cancels, "stream-sd")
|
||||
} else {
|
||||
log.Log.Error("Streaming sd does not exists for " + clientID)
|
||||
}
|
||||
|
||||
// Send stop streaming message
|
||||
log.Log.Info("ForwardSDStream: stop sending streaming over websocket")
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
"react/forbid-prop-types": "off",
|
||||
"no-case-declarations": "off",
|
||||
"camelcase": "off",
|
||||
"dot-notation": "off",
|
||||
"jsx-a11y/media-has-caption": "off",
|
||||
"jsx-a11y/anchor-is-valid": "off",
|
||||
"jsx-a11y/click-events-have-key-events": "off",
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"private": false,
|
||||
"dependencies": {
|
||||
"@giantmachines/redux-websocket": "^1.5.1",
|
||||
"@kerberos-io/ui": "^1.72.0",
|
||||
"@kerberos-io/ui": "^1.76.0",
|
||||
"@material-ui/core": "^4.12.4",
|
||||
"@material-ui/icons": "^4.11.3",
|
||||
"@testing-library/jest-dom": "^5.16.5",
|
||||
|
||||
5
ui/public/env.demo.js
Normal file
5
ui/public/env.demo.js
Normal file
@@ -0,0 +1,5 @@
|
||||
(function (window) {
|
||||
window['env'] = window['env'] || {};
|
||||
// Environment variables
|
||||
window['env']['mode'] = 'demo';
|
||||
})(this);
|
||||
5
ui/public/env.js
Normal file
5
ui/public/env.js
Normal file
@@ -0,0 +1,5 @@
|
||||
(function (window) {
|
||||
window['env'] = window['env'] || {};
|
||||
// Environment variables
|
||||
window['env']['mode'] = 'release';
|
||||
})(this);
|
||||
@@ -19,7 +19,7 @@
|
||||
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
|
||||
-->
|
||||
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
||||
<script src="assets/env.js"></script>
|
||||
<script src="%PUBLIC_URL%/env.js"></script>
|
||||
<!--
|
||||
Notice the use of %PUBLIC_URL% in the tags above.
|
||||
It will be replaced with the URL of the `public` folder during the build.
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
"configure": "Konfigurieren"
|
||||
},
|
||||
"buttons": {
|
||||
"save": "Speichern"
|
||||
"save": "Speichern",
|
||||
"verify_connection": "Verify Connection"
|
||||
},
|
||||
"navigation": {
|
||||
"profile": "Profil",
|
||||
@@ -69,7 +70,10 @@
|
||||
"verify_persistence_error": "Beim überprüfen der Speichereinstellungen ist ein Fehler aufgetreten",
|
||||
"verify_camera": "Überprüfen der Kameraeinstellungen.",
|
||||
"verify_camera_success": "Die Kameraeinstellungen wurden erfolgreich überprüft.",
|
||||
"verify_camera_error": "Beim überprüfen der Kameraeinstellungen ist ein Fehler aufgetreten"
|
||||
"verify_camera_error": "Beim überprüfen der Kameraeinstellungen ist ein Fehler aufgetreten",
|
||||
"verify_onvif": "Verifying your ONVIF settings.",
|
||||
"verify_onvif_success": "ONVIF settings are successfully verified.",
|
||||
"verify_onvif_error": "Something went wrong while verifying the ONVIF settings"
|
||||
},
|
||||
"overview": {
|
||||
"general": "Allgemein",
|
||||
@@ -186,13 +190,13 @@
|
||||
"kerberoshub_description_region": "Die Region in der die Aufnahmen gespeichert werden sollen.",
|
||||
"kerberoshub_bucket": "Bucket",
|
||||
"kerberoshub_description_bucket": "Der Bucket in dem die Aufnahmen gespeichert werden.",
|
||||
"kerberoshub_username": "Benutzername/Verzeichnis",
|
||||
"kerberoshub_username": "Benutzername/Verzeichnis (should match Kerberos Hub username)",
|
||||
"kerberoshub_description_username": "Der Benutzername des Kerberos Hub Accounts.",
|
||||
"kerberosvault_apiurl": "Kerberos Vault API URL",
|
||||
"kerberosvault_description_apiurl": "Die Kerberos Vault API URL.",
|
||||
"kerberosvault_provider": "Anbieter",
|
||||
"kerberosvault_description_provider": "Der Anbieter zu dem die Aufnahmen gesendet werden.",
|
||||
"kerberosvault_directory": "Verzeichnis",
|
||||
"kerberosvault_directory": "Verzeichnis (should match Kerberos Hub username)",
|
||||
"kerberosvault_description_directory": "Das Uneterverzeichnis in dem die Aufnahmen gespeichert werden sollen.",
|
||||
"kerberosvault_accesskey": "Zugriffsschlüssel",
|
||||
"kerberosvault_description_accesskey": "Der Zugriffsschlüssel des Kerberos Vault Accounts..",
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
"configure": "Configure"
|
||||
},
|
||||
"buttons": {
|
||||
"save": "Save"
|
||||
"save": "Save",
|
||||
"verify_connection": "Verify Connection"
|
||||
},
|
||||
"navigation": {
|
||||
"profile": "Profile",
|
||||
@@ -69,7 +70,10 @@
|
||||
"verify_persistence_error": "Something went wrong while verifying the persistence",
|
||||
"verify_camera": "Verifying your camera settings.",
|
||||
"verify_camera_success": "Camera settings are successfully verified.",
|
||||
"verify_camera_error": "Something went wrong while verifying the camera settings"
|
||||
"verify_camera_error": "Something went wrong while verifying the camera settings",
|
||||
"verify_onvif": "Verifying your ONVIF settings.",
|
||||
"verify_onvif_success": "ONVIF settings are successfully verified.",
|
||||
"verify_onvif_error": "Something went wrong while verifying the ONVIF settings"
|
||||
},
|
||||
"overview": {
|
||||
"general": "General",
|
||||
@@ -186,13 +190,13 @@
|
||||
"kerberoshub_description_region": "The region we are storing our recordings in.",
|
||||
"kerberoshub_bucket": "Bucket",
|
||||
"kerberoshub_description_bucket": "The bucket we are storing our recordings in.",
|
||||
"kerberoshub_username": "Username/Directory",
|
||||
"kerberoshub_username": "Username/Directory (should match Kerberos Hub username)",
|
||||
"kerberoshub_description_username": "The username of your Kerberos Hub account.",
|
||||
"kerberosvault_apiurl": "Kerberos Vault API URL",
|
||||
"kerberosvault_description_apiurl": "The Kerberos Vault API",
|
||||
"kerberosvault_provider": "Provider",
|
||||
"kerberosvault_description_provider": "The provider to which your recordings will be send.",
|
||||
"kerberosvault_directory": "Directory",
|
||||
"kerberosvault_directory": "Directory (should match Kerberos Hub username)",
|
||||
"kerberosvault_description_directory": "Sub directory the recordings will be stored in your provider.",
|
||||
"kerberosvault_accesskey": "Access key",
|
||||
"kerberosvault_description_accesskey": "The access key of your Kerberos Vault account.",
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
"configure": "Configure"
|
||||
},
|
||||
"buttons": {
|
||||
"save": "Save"
|
||||
"save": "Save",
|
||||
"verify_connection": "Verify Connection"
|
||||
},
|
||||
"navigation": {
|
||||
"profile": "Perfil",
|
||||
@@ -69,7 +70,10 @@
|
||||
"verify_persistence_error": "Something went wrong while verifying the persistence",
|
||||
"verify_camera": "Verifying your camera settings.",
|
||||
"verify_camera_success": "Camera settings are successfully verified.",
|
||||
"verify_camera_error": "Something went wrong while verifying the camera settings"
|
||||
"verify_camera_error": "Something went wrong while verifying the camera settings",
|
||||
"verify_onvif": "Verifying your ONVIF settings.",
|
||||
"verify_onvif_success": "ONVIF settings are successfully verified.",
|
||||
"verify_onvif_error": "Something went wrong while verifying the ONVIF settings"
|
||||
},
|
||||
"overview": {
|
||||
"general": "General",
|
||||
@@ -186,13 +190,13 @@
|
||||
"kerberoshub_description_region": "The region we are storing our recordings in.",
|
||||
"kerberoshub_bucket": "Bucket",
|
||||
"kerberoshub_description_bucket": "The bucket we are storing our recordings in.",
|
||||
"kerberoshub_username": "Username/Directory",
|
||||
"kerberoshub_username": "Username/Directory (should match Kerberos Hub username)",
|
||||
"kerberoshub_description_username": "The username of your Kerberos Hub account.",
|
||||
"kerberosvault_apiurl": "Kerberos Vault API URL",
|
||||
"kerberosvault_description_apiurl": "The Kerberos Vault API",
|
||||
"kerberosvault_provider": "Provider",
|
||||
"kerberosvault_description_provider": "The provider to which your recordings will be send.",
|
||||
"kerberosvault_directory": "Directory",
|
||||
"kerberosvault_directory": "Directory (should match Kerberos Hub username)",
|
||||
"kerberosvault_description_directory": "Sub directory the recordings will be stored in your provider.",
|
||||
"kerberosvault_accesskey": "Access key",
|
||||
"kerberosvault_description_accesskey": "The access key of your Kerberos Vault account.",
|
||||
|
||||
@@ -69,7 +69,10 @@
|
||||
"verify_persistence_error": "Quelque chose s'est mal déroulé lors de la vérification de la persistance",
|
||||
"verify_camera": "Vérifier les paramètres de votre caméra.",
|
||||
"verify_camera_success": "Les paramètres de la caméra sont vérifiés avec succès.",
|
||||
"verify_camera_error": "Quelque chose s'est mal déroulé lors de la vérification des paramètres de la caméra"
|
||||
"verify_camera_error": "Quelque chose s'est mal déroulé lors de la vérification des paramètres de la caméra",
|
||||
"verify_onvif": "Verifying your ONVIF settings.",
|
||||
"verify_onvif_success": "ONVIF settings are successfully verified.",
|
||||
"verify_onvif_error": "Something went wrong while verifying the ONVIF settings"
|
||||
},
|
||||
"overview": {
|
||||
"general": "Général",
|
||||
@@ -186,13 +189,13 @@
|
||||
"kerberoshub_description_region": "La région dans laquelle nous stockons vos enregistrements.",
|
||||
"kerberoshub_bucket": "Compartiment",
|
||||
"kerberoshub_description_bucket": "Le compartiment dans lequel nous stockons vos enregistrements.",
|
||||
"kerberoshub_username": "Utilisateur/Répertoire",
|
||||
"kerberoshub_username": "Utilisateur/Répertoire (should match Kerberos Hub username)",
|
||||
"kerberoshub_description_username": "Le nom d'utilisateur de votre compte Kerberos Hub.",
|
||||
"kerberosvault_apiurl": "URL de l'API Kerberos Vault",
|
||||
"kerberosvault_description_apiurl": "L'API Kerberos Vault",
|
||||
"kerberosvault_provider": "Fournisseur",
|
||||
"kerberosvault_description_provider": "Le fournisseur auquel vos enregistrements seront envoyés.",
|
||||
"kerberosvault_directory": "Répertoire",
|
||||
"kerberosvault_directory": "Répertoire (should match Kerberos Hub username)",
|
||||
"kerberosvault_description_directory": "Le sous-répertoire dans lequel les enregistrements seront stockés chez votre fournisseur.",
|
||||
"kerberosvault_accesskey": "Clé d'accès",
|
||||
"kerberosvault_description_accesskey": "La clé d'accès de votre compte Kerberos Vault.",
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
"configure": "設定"
|
||||
},
|
||||
"buttons": {
|
||||
"save": "保存"
|
||||
"save": "保存",
|
||||
"verify_connection": "Verify Connection"
|
||||
},
|
||||
"navigation": {
|
||||
"profile": "プロフィール",
|
||||
@@ -69,7 +70,10 @@
|
||||
"verify_persistence_error": "持続性の検証中に問題が発生しました",
|
||||
"verify_camera": "カメラの設定を確認しています。",
|
||||
"verify_camera_success": "カメラの設定が正常に検証されました。",
|
||||
"verify_camera_error": "カメラ設定の確認中に問題が発生しました"
|
||||
"verify_camera_error": "カメラ設定の確認中に問題が発生しました",
|
||||
"verify_onvif": "Verifying your ONVIF settings.",
|
||||
"verify_onvif_success": "ONVIF settings are successfully verified.",
|
||||
"verify_onvif_error": "Something went wrong while verifying the ONVIF settings"
|
||||
},
|
||||
"overview": {
|
||||
"general": "全般的",
|
||||
@@ -186,13 +190,13 @@
|
||||
"kerberoshub_description_region": "録音を保存しているリージョン。",
|
||||
"kerberoshub_bucket": "bucket",
|
||||
"kerberoshub_description_bucket": "録音を保存しているbucket",
|
||||
"kerberoshub_username": "ユーザー名/ディレクトリ",
|
||||
"kerberoshub_username": "ユーザー名/ディレクトリ (should match Kerberos Hub username)",
|
||||
"kerberoshub_description_username": "Kerberos Hub アカウントのユーザー名。",
|
||||
"kerberosvault_apiurl": "Kerberos ボールト API URL",
|
||||
"kerberosvault_description_apiurl": "Kerberos ボールト API",
|
||||
"kerberosvault_provider": "プロバイダ",
|
||||
"kerberosvault_description_provider": "録音の送信先のプロバイダー。",
|
||||
"kerberosvault_directory": "ディレクトリ",
|
||||
"kerberosvault_directory": "ディレクトリ (should match Kerberos Hub username)",
|
||||
"kerberosvault_description_directory": "録音がプロバイダーに保存されるサブディレクトリ。",
|
||||
"kerberosvault_accesskey": "アクセスキー",
|
||||
"kerberosvault_description_accesskey": "Kerberos Vault アカウントのアクセス キー。",
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
"configure": "Instellingen"
|
||||
},
|
||||
"buttons": {
|
||||
"save": "Opslaan"
|
||||
"save": "Opslaan",
|
||||
"verify_connection": "Verifieer Connectie"
|
||||
},
|
||||
"navigation": {
|
||||
"profile": "Profiel",
|
||||
@@ -69,7 +70,10 @@
|
||||
"verify_persistence_error": "Er ging iets fout tijdens het controleren van de opslag instellingen",
|
||||
"verify_camera": "We controleren de camera instellingen.",
|
||||
"verify_camera_success": "De camera instellingen zijn gevalideerd.",
|
||||
"verify_camera_error": "Er ging iets mis met de camera instellingen."
|
||||
"verify_camera_error": "Er ging iets mis met de camera instellingen.",
|
||||
"verify_onvif": "Verifying your ONVIF settings.",
|
||||
"verify_onvif_success": "ONVIF settings are successfully verified.",
|
||||
"verify_onvif_error": "Something went wrong while verifying the ONVIF settings"
|
||||
},
|
||||
"overview": {
|
||||
"general": "Algemeen",
|
||||
@@ -187,13 +191,13 @@
|
||||
"kerberoshub_description_region": "De regio waar jouw opnames worden opgeslagen.",
|
||||
"kerberoshub_bucket": "Bucket",
|
||||
"kerberoshub_description_bucket": "De bucket opslag locatie",
|
||||
"kerberoshub_username": "Gebruikersnaam/Map",
|
||||
"kerberoshub_username": "Gebruikersnaam/Map (moet overeenkomen met de Kerberos Hub username)",
|
||||
"kerberoshub_description_username": "De gebruikersnaam van jouw Kerberos Hub account.",
|
||||
"kerberosvault_apiurl": "Kerberos Vault API URL",
|
||||
"kerberosvault_description_apiurl": "De Kerberos Vault API",
|
||||
"kerberosvault_provider": "Provider",
|
||||
"kerberosvault_description_provider": "De provider verantwoordelijk voor de opnames op te slaan.",
|
||||
"kerberosvault_directory": "Map",
|
||||
"kerberosvault_directory": "Map (moet overeenkomen met de Kerberos Hub username)",
|
||||
"kerberosvault_description_directory": "Sub map waarin de opnames worden opgeslagen.",
|
||||
"kerberosvault_accesskey": "Access key",
|
||||
"kerberosvault_description_accesskey": "De access key van jouw Kerberos Vault account.",
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
"configure": "Configure"
|
||||
},
|
||||
"buttons": {
|
||||
"save": "Save"
|
||||
"save": "Save",
|
||||
"verify_connection": "Verify Connection"
|
||||
},
|
||||
"navigation": {
|
||||
"profile": "Profil",
|
||||
@@ -69,7 +70,10 @@
|
||||
"verify_persistence_error": "Something went wrong while verifying the persistence",
|
||||
"verify_camera": "Verifying your camera settings.",
|
||||
"verify_camera_success": "Camera settings are successfully verified.",
|
||||
"verify_camera_error": "Something went wrong while verifying the camera settings"
|
||||
"verify_camera_error": "Something went wrong while verifying the camera settings",
|
||||
"verify_onvif": "Verifying your ONVIF settings.",
|
||||
"verify_onvif_success": "ONVIF settings are successfully verified.",
|
||||
"verify_onvif_error": "Something went wrong while verifying the ONVIF settings"
|
||||
},
|
||||
"overview": {
|
||||
"general": "General",
|
||||
@@ -186,13 +190,13 @@
|
||||
"kerberoshub_description_region": "The region we are storing our recordings in.",
|
||||
"kerberoshub_bucket": "Bucket",
|
||||
"kerberoshub_description_bucket": "The bucket we are storing our recordings in.",
|
||||
"kerberoshub_username": "Username/Directory",
|
||||
"kerberoshub_username": "Username/Directory (should match Kerberos Hub username)",
|
||||
"kerberoshub_description_username": "The username of your Kerberos Hub account.",
|
||||
"kerberosvault_apiurl": "Kerberos Vault API URL",
|
||||
"kerberosvault_description_apiurl": "The Kerberos Vault API",
|
||||
"kerberosvault_provider": "Provider",
|
||||
"kerberosvault_description_provider": "The provider to which your recordings will be send.",
|
||||
"kerberosvault_directory": "Directory",
|
||||
"kerberosvault_directory": "Directory (should match Kerberos Hub username)",
|
||||
"kerberosvault_description_directory": "Sub directory the recordings will be stored in your provider.",
|
||||
"kerberosvault_accesskey": "Access key",
|
||||
"kerberosvault_description_accesskey": "The access key of your Kerberos Vault account.",
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
"configure": "Configurar"
|
||||
},
|
||||
"buttons": {
|
||||
"save": "Salvar"
|
||||
"save": "Salvar",
|
||||
"verify_connection": "Verify Connection"
|
||||
},
|
||||
"navigation": {
|
||||
"profile": "Perfil",
|
||||
@@ -69,7 +70,10 @@
|
||||
"verify_persistence_error": "Algo deu errado ao verificar o armazenamento",
|
||||
"verify_camera": "Verificando as configurações de sua câmera.",
|
||||
"verify_camera_success": "As configurações da câmera foram verificadas com sucesso.",
|
||||
"verify_camera_error": "Algo deu errado ao verificar as configurações da câmera."
|
||||
"verify_camera_error": "Algo deu errado ao verificar as configurações da câmera.",
|
||||
"verify_onvif": "Verifying your ONVIF settings.",
|
||||
"verify_onvif_success": "ONVIF settings are successfully verified.",
|
||||
"verify_onvif_error": "Something went wrong while verifying the ONVIF settings"
|
||||
},
|
||||
"overview": {
|
||||
"general": "Geral",
|
||||
@@ -186,13 +190,13 @@
|
||||
"kerberoshub_description_region": "A região em que estamos armazenando nossas gravações.",
|
||||
"kerberoshub_bucket": "Bucket",
|
||||
"kerberoshub_description_bucket": "O bucket no qual estamos armazenando nossas gravações.",
|
||||
"kerberoshub_username": "Nome de usuário/diretório (Username/Directory)",
|
||||
"kerberoshub_username": "Nome de usuário/diretório (should match Kerberos Hub username)",
|
||||
"kerberoshub_description_username": "O nome de usuário da sua conta do Kerberos Hub.",
|
||||
"kerberosvault_apiurl": "Url da API do Kerberos Vault",
|
||||
"kerberosvault_description_apiurl": "a API Kerberos Vault",
|
||||
"kerberosvault_provider": "Provedor",
|
||||
"kerberosvault_description_provider": "O provedor para o qual suas gravações serão enviadas.",
|
||||
"kerberosvault_directory": "Diretório",
|
||||
"kerberosvault_directory": "Diretório (should match Kerberos Hub username)",
|
||||
"kerberosvault_description_directory": "Subdiretório as gravações serão armazenadas em seu provedor.",
|
||||
"kerberosvault_accesskey": "Chave de acesso(Access key)",
|
||||
"kerberosvault_description_accesskey": "A chave de acesso da sua conta do Kerberos Vault.",
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
"configure": "配置"
|
||||
},
|
||||
"buttons": {
|
||||
"save": "保存"
|
||||
"save": "保存",
|
||||
"verify_connection": "Verify Connection"
|
||||
},
|
||||
"navigation": {
|
||||
"profile": "配置文件",
|
||||
@@ -69,7 +70,10 @@
|
||||
"verify_persistence_error": "验证持久化存储时出错",
|
||||
"verify_camera": "验证您的相机设置",
|
||||
"verify_camera_success": "相机设置验证成功",
|
||||
"verify_camera_error": "验证相机设置时出错"
|
||||
"verify_camera_error": "验证相机设置时出错",
|
||||
"verify_onvif": "Verifying your ONVIF settings.",
|
||||
"verify_onvif_success": "ONVIF settings are successfully verified.",
|
||||
"verify_onvif_error": "Something went wrong while verifying the ONVIF settings"
|
||||
},
|
||||
"overview": {
|
||||
"general": "常规",
|
||||
@@ -186,13 +190,13 @@
|
||||
"kerberoshub_description_region": "存储录像的区域",
|
||||
"kerberoshub_bucket": "Bucket",
|
||||
"kerberoshub_description_bucket": "存储录像的桶",
|
||||
"kerberoshub_username": "账户/目录",
|
||||
"kerberoshub_username": "账户/目录 (should match Kerberos Hub username)",
|
||||
"kerberoshub_description_username": "您的 Kerberos Hub 帐户的用户名",
|
||||
"kerberosvault_apiurl": "Kerberos Vault API URL",
|
||||
"kerberosvault_description_apiurl": "Kerberos Vault API",
|
||||
"kerberosvault_provider": "供应商",
|
||||
"kerberosvault_description_provider": "您的录像将会被发送到的提供商",
|
||||
"kerberosvault_directory": "目录",
|
||||
"kerberosvault_directory": "目录 (should match Kerberos Hub username)",
|
||||
"kerberosvault_description_directory": "录像将存储在提供商中的子目录",
|
||||
"kerberosvault_accesskey": "访问密钥",
|
||||
"kerberosvault_description_accesskey": "Kerberos Vault 帐户的访问密钥",
|
||||
|
||||
220
ui/src/App.jsx
220
ui/src/App.jsx
@@ -38,6 +38,16 @@ class App extends React.Component {
|
||||
dispatchGetDashboardInformation();
|
||||
dispatchConnect();
|
||||
|
||||
const connectInterval = interval(1000);
|
||||
this.connectionSubscription = connectInterval.subscribe(() => {
|
||||
const { connected } = this.props;
|
||||
if (connected) {
|
||||
// Already connected
|
||||
} else {
|
||||
dispatchConnect();
|
||||
}
|
||||
});
|
||||
|
||||
const interval$ = interval(5000);
|
||||
this.subscription = interval$.subscribe(() => {
|
||||
dispatchGetDashboardInformation();
|
||||
@@ -64,6 +74,7 @@ class App extends React.Component {
|
||||
|
||||
componentWillUnmount() {
|
||||
this.subscription.unsubscribe();
|
||||
this.connectionSubscription.unsubscribe();
|
||||
const message = {
|
||||
client_id: uuid(),
|
||||
message_type: 'goodbye',
|
||||
@@ -82,111 +93,122 @@ class App extends React.Component {
|
||||
const { children, username, dashboard, dispatchLogout } = this.props;
|
||||
const cloudOnline = this.getCurrentTimestamp() - dashboard.cloudOnline < 30;
|
||||
return (
|
||||
<div id="page-root">
|
||||
<Sidebar logo={logo} title="Kerberos Agent" version="v1-beta" mobile>
|
||||
<Profilebar
|
||||
username={username}
|
||||
email="support@kerberos.io"
|
||||
userrole={t('navigation.admin')}
|
||||
logout={dispatchLogout}
|
||||
/>
|
||||
<Navigation>
|
||||
<NavigationSection title={t('navigation.management')} />
|
||||
<NavigationGroup>
|
||||
<NavigationItem
|
||||
title={t('navigation.dashboard')}
|
||||
icon="dashboard"
|
||||
link="dashboard"
|
||||
/>
|
||||
<NavigationItem
|
||||
title={t('navigation.recordings')}
|
||||
icon="media"
|
||||
link="media"
|
||||
/>
|
||||
<NavigationItem
|
||||
title={t('navigation.settings')}
|
||||
icon="preferences"
|
||||
link="settings"
|
||||
/>
|
||||
</NavigationGroup>
|
||||
<NavigationSection title={t('navigation.help_support')} />
|
||||
<NavigationGroup>
|
||||
<NavigationItem
|
||||
title={t('navigation.swagger')}
|
||||
icon="api"
|
||||
external
|
||||
link={`${config.URL}/swagger/index.html`}
|
||||
/>
|
||||
<NavigationItem
|
||||
title={t('navigation.documentation')}
|
||||
icon="book"
|
||||
external
|
||||
link="https://doc.kerberos.io/agent/announcement"
|
||||
/>
|
||||
<NavigationItem
|
||||
title="Kerberos Hub"
|
||||
icon="cloud"
|
||||
external
|
||||
link="https://app.kerberos.io"
|
||||
/>
|
||||
<NavigationItem
|
||||
title={t('navigation.ui_library')}
|
||||
icon="paint"
|
||||
external
|
||||
link="https://ui.kerberos.io/"
|
||||
/>
|
||||
<NavigationItem
|
||||
title="Github"
|
||||
icon="github-nav"
|
||||
external
|
||||
link="https://github.com/kerberos-io/agent"
|
||||
/>
|
||||
</NavigationGroup>
|
||||
<NavigationSection title={t('navigation.layout')} />
|
||||
<NavigationGroup>
|
||||
<LanguageSelect />
|
||||
</NavigationGroup>
|
||||
|
||||
<NavigationSection title="Websocket" />
|
||||
<NavigationGroup>
|
||||
<div className="websocket-badge">
|
||||
<Badge
|
||||
title={connected ? 'connected' : 'disconnected'}
|
||||
status={connected ? 'success' : 'warning'}
|
||||
<>
|
||||
{config.MODE !== 'release' && (
|
||||
<div className={`environment ${config.MODE}`}>
|
||||
Environment: {config.MODE}
|
||||
</div>
|
||||
)}
|
||||
<div id="page-root">
|
||||
<Sidebar logo={logo} title="Kerberos Agent" version="v1-beta" mobile>
|
||||
<Profilebar
|
||||
username={username}
|
||||
email="support@kerberos.io"
|
||||
userrole={t('navigation.admin')}
|
||||
logout={dispatchLogout}
|
||||
/>
|
||||
<Navigation>
|
||||
<NavigationSection title={t('navigation.management')} />
|
||||
<NavigationGroup>
|
||||
<NavigationItem
|
||||
title={t('navigation.dashboard')}
|
||||
icon="dashboard"
|
||||
link="dashboard"
|
||||
/>
|
||||
</div>
|
||||
</NavigationGroup>
|
||||
</Navigation>
|
||||
</Sidebar>
|
||||
<Main>
|
||||
<Gradient />
|
||||
<NavigationItem
|
||||
title={t('navigation.recordings')}
|
||||
icon="media"
|
||||
link="media"
|
||||
/>
|
||||
<NavigationItem
|
||||
title={t('navigation.settings')}
|
||||
icon="preferences"
|
||||
link="settings"
|
||||
/>
|
||||
</NavigationGroup>
|
||||
<NavigationSection title={t('navigation.help_support')} />
|
||||
<NavigationGroup>
|
||||
<NavigationItem
|
||||
title={t('navigation.swagger')}
|
||||
icon="api"
|
||||
external
|
||||
link={`${config.URL}/swagger/index.html`}
|
||||
/>
|
||||
<NavigationItem
|
||||
title={t('navigation.documentation')}
|
||||
icon="book"
|
||||
external
|
||||
link="https://doc.kerberos.io/agent/announcement"
|
||||
/>
|
||||
<NavigationItem
|
||||
title="Kerberos Hub"
|
||||
icon="cloud"
|
||||
external
|
||||
link="https://app.kerberos.io"
|
||||
/>
|
||||
<NavigationItem
|
||||
title={t('navigation.ui_library')}
|
||||
icon="paint"
|
||||
external
|
||||
link="https://ui.kerberos.io/"
|
||||
/>
|
||||
<NavigationItem
|
||||
title="Github"
|
||||
icon="github-nav"
|
||||
external
|
||||
link="https://github.com/kerberos-io/agent"
|
||||
/>
|
||||
</NavigationGroup>
|
||||
<NavigationSection title={t('navigation.layout')} />
|
||||
<NavigationGroup>
|
||||
<LanguageSelect />
|
||||
</NavigationGroup>
|
||||
|
||||
{!cloudOnline && (
|
||||
<a href="https://app.kerberos.io" target="_blank" rel="noreferrer">
|
||||
<div className="cloud-not-installed">
|
||||
<div>
|
||||
<Icon label="cloud" />
|
||||
Activate Kerberos Hub, and make your cameras and recordings
|
||||
available through a secured cloud!
|
||||
<NavigationSection title="Websocket" />
|
||||
<NavigationGroup>
|
||||
<div className="websocket-badge">
|
||||
<Badge
|
||||
title={connected ? 'connected' : 'disconnected'}
|
||||
status={connected ? 'success' : 'warning'}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
)}
|
||||
</NavigationGroup>
|
||||
</Navigation>
|
||||
</Sidebar>
|
||||
<Main>
|
||||
<Gradient />
|
||||
|
||||
{dashboard.offlineMode === 'true' && (
|
||||
<Link to="/settings">
|
||||
<div className="offline-mode">
|
||||
<div>
|
||||
<Icon label="info" />
|
||||
Attention! Kerberos is currently running in Offline mode.
|
||||
{!cloudOnline && (
|
||||
<a
|
||||
href="https://app.kerberos.io"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
<div className="cloud-not-installed">
|
||||
<div>
|
||||
<Icon label="cloud" />
|
||||
Activate Kerberos Hub, and make your cameras and recordings
|
||||
available through a secured cloud!
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
)}
|
||||
</a>
|
||||
)}
|
||||
|
||||
<MainBody>{children}</MainBody>
|
||||
</Main>
|
||||
</div>
|
||||
{dashboard.offlineMode === 'true' && (
|
||||
<Link to="/settings">
|
||||
<div className="offline-mode">
|
||||
<div>
|
||||
<Icon label="info" />
|
||||
Attention! Kerberos is currently running in Offline mode.
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
)}
|
||||
|
||||
<MainBody>{children}</MainBody>
|
||||
</Main>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import {
|
||||
doGetConfig,
|
||||
doSaveConfig,
|
||||
doVerifyOnvif,
|
||||
doVerifyHub,
|
||||
doVerifyPersistence,
|
||||
doGetKerberosAgentTags,
|
||||
@@ -39,6 +40,28 @@ export const updateRegion = (id, polygon) => {
|
||||
};
|
||||
};
|
||||
|
||||
export const verifyOnvif = (config, onSuccess, onError) => {
|
||||
return (dispatch) => {
|
||||
doVerifyOnvif(
|
||||
config,
|
||||
(data) => {
|
||||
dispatch({
|
||||
type: 'VERIFY_ONVIF',
|
||||
});
|
||||
if (onSuccess) {
|
||||
onSuccess(data);
|
||||
}
|
||||
},
|
||||
(error) => {
|
||||
const { message } = error;
|
||||
if (onError) {
|
||||
onError(message);
|
||||
}
|
||||
}
|
||||
);
|
||||
};
|
||||
};
|
||||
|
||||
export const verifyCamera = (streamType, config, onSuccess, onError) => {
|
||||
return (dispatch) => {
|
||||
doVerifyCamera(
|
||||
|
||||
@@ -91,6 +91,26 @@ export function doVerifyHub(config, onSuccess, onError) {
|
||||
});
|
||||
}
|
||||
|
||||
export function doVerifyOnvif(config, onSuccess, onError) {
|
||||
const endpoint = API.post(`onvif/verify`, {
|
||||
...config,
|
||||
});
|
||||
endpoint
|
||||
.then((res) => {
|
||||
if (res.status !== 200) {
|
||||
throw new Error(res.data);
|
||||
}
|
||||
return res.data;
|
||||
})
|
||||
.then((data) => {
|
||||
onSuccess(data);
|
||||
})
|
||||
.catch((e) => {
|
||||
const { data } = e.response;
|
||||
onError(data);
|
||||
});
|
||||
}
|
||||
|
||||
export function doVerifyCamera(streamType, config, onSuccess, onError) {
|
||||
const cameraStreams = {
|
||||
rtsp: '',
|
||||
|
||||
@@ -9,9 +9,10 @@ const dev = {
|
||||
ENV: 'dev',
|
||||
// Comment the below lines, when using codespaces or other special DNS names (which you can't control)
|
||||
HOSTNAME: hostname,
|
||||
API_URL: `${protocol}//${hostname}:8080/api`,
|
||||
URL: `${protocol}//${hostname}:8080`,
|
||||
WS_URL: `${websocketprotocol}//${hostname}:8080/ws`,
|
||||
API_URL: `${protocol}//${hostname}:80/api`,
|
||||
URL: `${protocol}//${hostname}:80`,
|
||||
WS_URL: `${websocketprotocol}//${hostname}:80/ws`,
|
||||
MODE: window['env']['mode'],
|
||||
// Uncomment, and comment the above lines, when using codespaces or other special DNS names (which you can't control)
|
||||
// HOSTNAME: externalHost,
|
||||
// API_URL: `${protocol}//${externalHost}/api`,
|
||||
@@ -25,6 +26,7 @@ const prod = {
|
||||
API_URL: `${protocol}//${host}/api`,
|
||||
URL: `${protocol}//${host}`,
|
||||
WS_URL: `${websocketprotocol}//${host}/ws`,
|
||||
MODE: window['env']['mode'],
|
||||
};
|
||||
|
||||
const config = process.env.REACT_APP_ENVIRONMENT === 'production' ? prod : dev;
|
||||
|
||||
@@ -50,7 +50,9 @@ function getAuthState() {
|
||||
}
|
||||
}
|
||||
|
||||
const reduxWebsocketMiddleware = reduxWebsocket();
|
||||
const reduxWebsocketMiddleware = reduxWebsocket({
|
||||
reconnectOnClose: true,
|
||||
});
|
||||
|
||||
const store = createStore(
|
||||
rootReducer(history),
|
||||
|
||||
@@ -144,3 +144,55 @@ body {
|
||||
padding: 0;
|
||||
font-family: 'Inter', sans-serif; // use regular Inter font by default..
|
||||
}
|
||||
|
||||
.environment.develop {
|
||||
background: repeating-linear-gradient(
|
||||
-55deg,
|
||||
#222,
|
||||
#222 10px,
|
||||
#035e1f 10px,
|
||||
#035e1f 20px
|
||||
);
|
||||
text-align: center;
|
||||
color: rgba(255, 255, 255, 0.75);
|
||||
padding: size(1) 0;
|
||||
}
|
||||
|
||||
.environment.demo {
|
||||
background: repeating-linear-gradient(
|
||||
-55deg,
|
||||
#222,
|
||||
#222 10px,
|
||||
#035e1f 10px,
|
||||
#035e1f 20px
|
||||
);
|
||||
text-align: center;
|
||||
color: rgba(255, 255, 255, 0.75);
|
||||
padding: 12px 0;
|
||||
}
|
||||
|
||||
.environment.staging {
|
||||
background: repeating-linear-gradient(
|
||||
-55deg,
|
||||
#222,
|
||||
#222 10px,
|
||||
#6d0e0e 10px,
|
||||
#6d0e0e 20px
|
||||
);
|
||||
text-align: center;
|
||||
color: white;
|
||||
padding: 12px 0;
|
||||
}
|
||||
|
||||
.environment.acceptance {
|
||||
background: repeating-linear-gradient(
|
||||
-55deg,
|
||||
#222,
|
||||
#222 10px,
|
||||
#8b8203 10px,
|
||||
#8b8203 20px
|
||||
);
|
||||
text-align: center;
|
||||
color: white;
|
||||
padding: 12px 0;
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import { Link, withRouter } from 'react-router-dom';
|
||||
import { withTranslation } from 'react-i18next';
|
||||
import { send } from '@giantmachines/redux-websocket';
|
||||
import { connect } from 'react-redux';
|
||||
import { interval } from 'rxjs';
|
||||
import {
|
||||
Breadcrumb,
|
||||
KPI,
|
||||
@@ -34,7 +35,9 @@ class Dashboard extends React.Component {
|
||||
liveviewLoaded: false,
|
||||
open: false,
|
||||
currentRecording: '',
|
||||
initialised: false,
|
||||
};
|
||||
this.initialiseLiveview = this.initialiseLiveview.bind(this);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
@@ -46,27 +49,11 @@ class Dashboard extends React.Component {
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
const { connected } = this.props;
|
||||
if (connected === true) {
|
||||
const { dispatchSend } = this.props;
|
||||
const message = {
|
||||
message_type: 'stream-sd',
|
||||
};
|
||||
dispatchSend(message);
|
||||
}
|
||||
this.initialiseLiveview();
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
const { connected: connectedPrev } = prevProps;
|
||||
const { connected } = this.props;
|
||||
if (connectedPrev === false && connected === true) {
|
||||
const { dispatchSend } = this.props;
|
||||
const message = {
|
||||
message_type: 'stream-sd',
|
||||
};
|
||||
dispatchSend(message);
|
||||
}
|
||||
componentDidUpdate() {
|
||||
this.initialiseLiveview();
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
@@ -75,6 +62,9 @@ class Dashboard extends React.Component {
|
||||
liveview[0].remove();
|
||||
}
|
||||
|
||||
if (this.requestStreamSubscription) {
|
||||
this.requestStreamSubscription.unsubscribe();
|
||||
}
|
||||
const { dispatchSend } = this.props;
|
||||
const message = {
|
||||
message_type: 'stop-sd',
|
||||
@@ -93,6 +83,30 @@ class Dashboard extends React.Component {
|
||||
return Math.round(Date.now() / 1000);
|
||||
}
|
||||
|
||||
initialiseLiveview() {
|
||||
const { initialised } = this.state;
|
||||
if (!initialised) {
|
||||
const message = {
|
||||
message_type: 'stream-sd',
|
||||
};
|
||||
const { connected, dispatchSend } = this.props;
|
||||
if (connected) {
|
||||
dispatchSend(message);
|
||||
}
|
||||
|
||||
const requestStreamInterval = interval(2000);
|
||||
this.requestStreamSubscription = requestStreamInterval.subscribe(() => {
|
||||
const { connected: isConnected } = this.props;
|
||||
if (isConnected) {
|
||||
dispatchSend(message);
|
||||
}
|
||||
});
|
||||
this.setState({
|
||||
initialised: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
openModal(file) {
|
||||
this.setState({
|
||||
open: true,
|
||||
@@ -106,17 +120,16 @@ class Dashboard extends React.Component {
|
||||
|
||||
// We check if the camera was getting a valid frame
|
||||
// during the last 5 seconds, otherwise we assume the camera is offline.
|
||||
const isCameraOnline =
|
||||
this.getCurrentTimestamp() - dashboard.cameraOnline < 15;
|
||||
const isCameraOnline = dashboard.cameraOnline;
|
||||
|
||||
// We check if a connection is made to Kerberos Hub, or if Offline mode
|
||||
// has been turned on.
|
||||
const cloudOnline = this.getCurrentTimestamp() - dashboard.cloudOnline < 30;
|
||||
const isCloudOnline = dashboard.cloudOnline;
|
||||
let cloudConnection = t('dashboard.not_connected');
|
||||
if (dashboard.offlineMode === 'true') {
|
||||
cloudConnection = t('dashboard.offline_mode');
|
||||
} else {
|
||||
cloudConnection = cloudOnline
|
||||
cloudConnection = isCloudOnline
|
||||
? t('dashboard.connected')
|
||||
: t('dashboard.not_connected');
|
||||
}
|
||||
@@ -176,7 +189,7 @@ class Dashboard extends React.Component {
|
||||
subtitle={cloudConnection}
|
||||
footer="Cloud"
|
||||
icon={
|
||||
cloudOnline && dashboard.offlineMode !== 'true'
|
||||
isCloudOnline && dashboard.offlineMode !== 'true'
|
||||
? 'circle-check-big'
|
||||
: 'circle-cross-big'
|
||||
}
|
||||
@@ -297,7 +310,7 @@ class Dashboard extends React.Component {
|
||||
</div>
|
||||
<div>
|
||||
<h2>{t('dashboard.live_view')}</h2>
|
||||
{!liveviewLoaded && (
|
||||
{(!liveviewLoaded || !isCameraOnline) && (
|
||||
<SetupBox
|
||||
btnicon="preferences"
|
||||
btnlabel={t('dashboard.configure_connection')}
|
||||
@@ -307,7 +320,12 @@ class Dashboard extends React.Component {
|
||||
text={t('dashboard.loading_live_view_description')}
|
||||
/>
|
||||
)}
|
||||
<div style={{ visibility: liveviewLoaded ? 'visible' : 'hidden' }}>
|
||||
<div
|
||||
style={{
|
||||
visibility:
|
||||
liveviewLoaded && isCameraOnline ? 'visible' : 'hidden',
|
||||
}}
|
||||
>
|
||||
<ImageCard
|
||||
imageSrc={`data:image/png;base64, ${
|
||||
images.length ? images[0] : ''
|
||||
|
||||
@@ -49,60 +49,98 @@ class Login extends React.Component {
|
||||
const { loginError, error } = this.props;
|
||||
|
||||
return (
|
||||
<LandingLayout
|
||||
title="Kerberos Agent"
|
||||
version={config.VERSION}
|
||||
description="Video surveillance for everyone"
|
||||
>
|
||||
<section className="login-body">
|
||||
<Block>
|
||||
<form onSubmit={this.handleSubmit} noValidate>
|
||||
<BlockHeader>
|
||||
<div>
|
||||
<Icon label="login" /> <h4>Login</h4>
|
||||
</div>
|
||||
</BlockHeader>
|
||||
{loginError && (
|
||||
<AlertMessage
|
||||
message={error}
|
||||
onClick={() => this.hideMessage()}
|
||||
/>
|
||||
)}
|
||||
<BlockBody>
|
||||
<Input
|
||||
label="username or email"
|
||||
placeholder="Your username/email"
|
||||
readonly={false}
|
||||
disabled={false}
|
||||
type="text"
|
||||
name="username"
|
||||
iconleft="accounts"
|
||||
/>
|
||||
<Input
|
||||
label="password"
|
||||
placeholder="Your password"
|
||||
readonly={false}
|
||||
disabled={false}
|
||||
type="password"
|
||||
name="password"
|
||||
iconleft="locked"
|
||||
iconright="activity"
|
||||
seperate
|
||||
/>
|
||||
</BlockBody>
|
||||
<BlockFooter>
|
||||
<p />
|
||||
<Button
|
||||
buttonType="submit"
|
||||
type="submit"
|
||||
icon="logout"
|
||||
label="Login"
|
||||
/>
|
||||
</BlockFooter>
|
||||
</form>
|
||||
</Block>
|
||||
</section>
|
||||
</LandingLayout>
|
||||
<>
|
||||
{config.MODE !== 'release' && (
|
||||
<div className={`environment ${config.MODE}`}>
|
||||
Environment: {config.MODE}
|
||||
</div>
|
||||
)}
|
||||
<LandingLayout
|
||||
title="Kerberos Agent"
|
||||
version={config.VERSION}
|
||||
description="Video surveillance for everyone"
|
||||
>
|
||||
<section className="login-body">
|
||||
<Block>
|
||||
<form onSubmit={this.handleSubmit} noValidate>
|
||||
<BlockHeader>
|
||||
<div>
|
||||
<Icon label="login" /> <h4>Login</h4>
|
||||
</div>
|
||||
</BlockHeader>
|
||||
{loginError && (
|
||||
<AlertMessage
|
||||
message={error}
|
||||
onClick={() => this.hideMessage()}
|
||||
/>
|
||||
)}
|
||||
<BlockBody>
|
||||
{config.MODE === 'demo' && (
|
||||
<>
|
||||
<Input
|
||||
label="username or email"
|
||||
placeholder="Your username/email"
|
||||
readonly
|
||||
disabled={false}
|
||||
value="root"
|
||||
type="text"
|
||||
name="username"
|
||||
iconleft="accounts"
|
||||
/>
|
||||
<Input
|
||||
label="password"
|
||||
placeholder="Your password"
|
||||
readonly
|
||||
disabled={false}
|
||||
value="root"
|
||||
type="password"
|
||||
name="password"
|
||||
iconleft="locked"
|
||||
iconright="activity"
|
||||
seperate
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
|
||||
{config.MODE !== 'demo' && (
|
||||
<>
|
||||
<Input
|
||||
label="username or email"
|
||||
placeholder="Your username/email"
|
||||
readonly={false}
|
||||
disabled={false}
|
||||
type="text"
|
||||
name="username"
|
||||
iconleft="accounts"
|
||||
/>
|
||||
<Input
|
||||
label="password"
|
||||
placeholder="Your password"
|
||||
readonly={false}
|
||||
disabled={false}
|
||||
type="password"
|
||||
name="password"
|
||||
iconleft="locked"
|
||||
iconright="activity"
|
||||
seperate
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</BlockBody>
|
||||
<BlockFooter>
|
||||
<p />
|
||||
<Button
|
||||
buttonType="submit"
|
||||
type="submit"
|
||||
icon="logout"
|
||||
label="Login"
|
||||
/>
|
||||
</BlockFooter>
|
||||
</form>
|
||||
</Block>
|
||||
</section>
|
||||
</LandingLayout>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ import {
|
||||
updateRegion,
|
||||
removeRegion,
|
||||
saveConfig,
|
||||
verifyOnvif,
|
||||
verifyCamera,
|
||||
verifyHub,
|
||||
verifyPersistence,
|
||||
@@ -63,6 +64,9 @@ class Settings extends React.Component {
|
||||
verifyCameraSuccess: false,
|
||||
verifyCameraError: false,
|
||||
verifyCameraMessage: '',
|
||||
verifyOnvifSuccess: false,
|
||||
verifyOnvifError: false,
|
||||
verifyOnvifErrorMessage: '',
|
||||
loading: false,
|
||||
loadingHub: false,
|
||||
loadingCamera: false,
|
||||
@@ -127,6 +131,7 @@ class Settings extends React.Component {
|
||||
this.onAddRegion = this.onAddRegion.bind(this);
|
||||
this.onUpdateRegion = this.onUpdateRegion.bind(this);
|
||||
this.onDeleteRegion = this.onDeleteRegion.bind(this);
|
||||
this.verifyONVIF = this.verifyONVIF.bind(this);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
@@ -224,13 +229,15 @@ class Settings extends React.Component {
|
||||
|
||||
calculateTimetable(timetable) {
|
||||
this.timetable = timetable;
|
||||
for (let i = 0; i < timetable.length; i += 1) {
|
||||
const time = timetable[i];
|
||||
const { start1, start2, end1, end2 } = time;
|
||||
this.timetable[i].start1Full = this.convertSecondsToHourMinute(start1);
|
||||
this.timetable[i].start2Full = this.convertSecondsToHourMinute(start2);
|
||||
this.timetable[i].end1Full = this.convertSecondsToHourMinute(end1);
|
||||
this.timetable[i].end2Full = this.convertSecondsToHourMinute(end2);
|
||||
if (this.timetable) {
|
||||
for (let i = 0; i < timetable.length; i += 1) {
|
||||
const time = timetable[i];
|
||||
const { start1, start2, end1, end2 } = time;
|
||||
this.timetable[i].start1Full = this.convertSecondsToHourMinute(start1);
|
||||
this.timetable[i].start2Full = this.convertSecondsToHourMinute(start2);
|
||||
this.timetable[i].end1Full = this.convertSecondsToHourMinute(end1);
|
||||
this.timetable[i].end2Full = this.convertSecondsToHourMinute(end2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -272,6 +279,8 @@ class Settings extends React.Component {
|
||||
verifyHubError: false,
|
||||
configSuccess: false,
|
||||
configError: false,
|
||||
verifyOnvifSuccess: false,
|
||||
verifyOnvifError: false,
|
||||
});
|
||||
|
||||
if (config) {
|
||||
@@ -293,6 +302,52 @@ class Settings extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
verifyONVIF() {
|
||||
const { config, dispatchVerifyOnvif } = this.props;
|
||||
|
||||
// Get camera configuration (subset of config).
|
||||
const cameraConfig = {
|
||||
onvif_xaddr: config.config.capture.ipcamera.onvif_xaddr,
|
||||
onvif_username: config.config.capture.ipcamera.onvif_username,
|
||||
onvif_password: config.config.capture.ipcamera.onvif_password,
|
||||
};
|
||||
|
||||
this.setState({
|
||||
verifyOnvifSuccess: false,
|
||||
verifyOnvifError: false,
|
||||
verifyOnvifErrorMessage: '',
|
||||
verifyCameraSuccess: false,
|
||||
verifyCameraError: false,
|
||||
verifyCameraErrorMessage: '',
|
||||
configSuccess: false,
|
||||
configError: false,
|
||||
loadingCamera: false,
|
||||
loadingOnvif: true,
|
||||
});
|
||||
|
||||
if (config) {
|
||||
dispatchVerifyOnvif(
|
||||
cameraConfig,
|
||||
() => {
|
||||
this.setState({
|
||||
verifyOnvifSuccess: true,
|
||||
verifyOnvifError: false,
|
||||
verifyOnvifErrorMessage: '',
|
||||
loadingOnvif: false,
|
||||
});
|
||||
},
|
||||
(error) => {
|
||||
this.setState({
|
||||
verifyOnvifSuccess: false,
|
||||
verifyOnvifError: true,
|
||||
verifyOnvifErrorMessage: error,
|
||||
loadingOnvif: false,
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
verifyHubSettings() {
|
||||
const { config, dispatchVerifyHub } = this.props;
|
||||
if (config) {
|
||||
@@ -315,6 +370,8 @@ class Settings extends React.Component {
|
||||
verifyCameraSuccess: false,
|
||||
verifyCameraError: false,
|
||||
verifyCameraErrorMessage: '',
|
||||
verifyOnvifSuccess: false,
|
||||
verifyOnvifError: false,
|
||||
loadingHub: true,
|
||||
});
|
||||
|
||||
@@ -360,6 +417,8 @@ class Settings extends React.Component {
|
||||
persistenceError: false,
|
||||
verifyCameraSuccess: false,
|
||||
verifyCameraError: false,
|
||||
verifyOnvifSuccess: false,
|
||||
verifyOnvifError: false,
|
||||
verifyCameraErrorMessage: '',
|
||||
loading: true,
|
||||
});
|
||||
@@ -400,6 +459,7 @@ class Settings extends React.Component {
|
||||
this.setState({
|
||||
configSuccess: false,
|
||||
configError: false,
|
||||
loadingCamera: true,
|
||||
verifyPersistenceSuccess: false,
|
||||
verifyPersistenceError: false,
|
||||
verifyHubSuccess: false,
|
||||
@@ -408,9 +468,10 @@ class Settings extends React.Component {
|
||||
verifyCameraSuccess: false,
|
||||
verifyCameraError: false,
|
||||
verifyCameraErrorMessage: '',
|
||||
verifyOnvifSuccess: false,
|
||||
verifyOnvifError: false,
|
||||
hubSuccess: false,
|
||||
hubError: false,
|
||||
loadingCamera: true,
|
||||
});
|
||||
|
||||
dispatchVerifyCamera(
|
||||
@@ -451,6 +512,10 @@ class Settings extends React.Component {
|
||||
verifyCameraSuccess,
|
||||
verifyCameraError,
|
||||
verifyCameraErrorMessage,
|
||||
loadingOnvif,
|
||||
verifyOnvifSuccess,
|
||||
verifyOnvifError,
|
||||
verifyOnvifErrorMessage,
|
||||
loadingCamera,
|
||||
loading,
|
||||
loadingHub,
|
||||
@@ -650,10 +715,23 @@ class Settings extends React.Component {
|
||||
type="alert"
|
||||
message={`${t(
|
||||
'settings.info.verify_camera_error'
|
||||
)} :${verifyCameraErrorMessage}`}
|
||||
)}: ${verifyCameraErrorMessage}`}
|
||||
/>
|
||||
)}
|
||||
|
||||
{loadingOnvif && (
|
||||
<InfoBar type="loading" message={t('settings.info.verify_onvif')} />
|
||||
)}
|
||||
{verifyOnvifSuccess && (
|
||||
<InfoBar
|
||||
type="success"
|
||||
message={t('settings.info.verify_onvif_success')}
|
||||
/>
|
||||
)}
|
||||
{verifyOnvifError && (
|
||||
<InfoBar type="alert" message={`${verifyOnvifErrorMessage}`} />
|
||||
)}
|
||||
|
||||
{loadingHub && (
|
||||
<InfoBar type="loading" message={t('settings.info.verify_hub')} />
|
||||
)}
|
||||
@@ -1101,7 +1179,7 @@ class Settings extends React.Component {
|
||||
noPadding
|
||||
label={t('settings.camera.onvif_xaddr')}
|
||||
value={config.capture.ipcamera.onvif_xaddr}
|
||||
placeholder="http://x.x.x.x/onvif/device_service"
|
||||
placeholder="x.x.x.x:yyyy"
|
||||
onChange={(value) =>
|
||||
this.onUpdateField(
|
||||
'capture.ipcamera',
|
||||
@@ -1141,6 +1219,12 @@ class Settings extends React.Component {
|
||||
/>
|
||||
</BlockBody>
|
||||
<BlockFooter>
|
||||
<Button
|
||||
label={t('buttons.verify_connection')}
|
||||
type="default"
|
||||
icon="verify"
|
||||
onClick={this.verifyONVIF}
|
||||
/>
|
||||
<Button
|
||||
label={t('buttons.save')}
|
||||
type="default"
|
||||
@@ -2030,17 +2114,6 @@ class Settings extends React.Component {
|
||||
/>
|
||||
{config.cloud === this.KERBEROS_HUB && (
|
||||
<>
|
||||
<Input
|
||||
noPadding
|
||||
label={t('settings.persistence.kerberoshub_proxyurl')}
|
||||
placeholder={t(
|
||||
'settings.persistence.kerberoshub_description_proxyurl'
|
||||
)}
|
||||
value={config.s3 ? config.s3.proxyuri : ''}
|
||||
onChange={(value) =>
|
||||
this.onUpdateField('s3', 'proxyuri', value, config.s3)
|
||||
}
|
||||
/>
|
||||
<Input
|
||||
noPadding
|
||||
label={t('settings.persistence.kerberoshub_region')}
|
||||
@@ -2052,28 +2125,6 @@ class Settings extends React.Component {
|
||||
this.onUpdateField('s3', 'region', value, config.s3)
|
||||
}
|
||||
/>
|
||||
<Input
|
||||
noPadding
|
||||
label={t('settings.persistence.kerberoshub_bucket')}
|
||||
placeholder={t(
|
||||
'settings.persistence.kerberoshub_description_bucket'
|
||||
)}
|
||||
value={config.s3 ? config.s3.bucket : ''}
|
||||
onChange={(value) =>
|
||||
this.onUpdateField('s3', 'bucket', value, config.s3)
|
||||
}
|
||||
/>
|
||||
<Input
|
||||
noPadding
|
||||
label={t('settings.persistence.kerberoshub_username')}
|
||||
placeholder={t(
|
||||
'settings.persistence.kerberoshub_description_username'
|
||||
)}
|
||||
value={config.s3 ? config.s3.username : ''}
|
||||
onChange={(value) =>
|
||||
this.onUpdateField('s3', 'username', value, config.s3)
|
||||
}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
{config.cloud === this.KERBEROS_VAULT && (
|
||||
@@ -2243,6 +2294,8 @@ const mapStateToProps = (state /* , ownProps */) => ({
|
||||
});
|
||||
|
||||
const mapDispatchToProps = (dispatch /* , ownProps */) => ({
|
||||
dispatchVerifyOnvif: (config, success, error) =>
|
||||
dispatch(verifyOnvif(config, success, error)),
|
||||
dispatchVerifyCamera: (streamType, config, success, error) =>
|
||||
dispatch(verifyCamera(streamType, config, success, error)),
|
||||
dispatchVerifyHub: (config, success, error) =>
|
||||
@@ -2270,6 +2323,7 @@ Settings.propTypes = {
|
||||
dispatchUpdateRegion: PropTypes.func.isRequired,
|
||||
dispatchRemoveRegion: PropTypes.func.isRequired,
|
||||
dispatchVerifyCamera: PropTypes.func.isRequired,
|
||||
dispatchVerifyOnvif: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default withTranslation()(
|
||||
|
||||
Reference in New Issue
Block a user