Version 6.1.7

This commit is contained in:
Chrystian Huot 2022-01-27 10:27:47 -05:00
parent 0534693937
commit 1eebbc1154
34 changed files with 288 additions and 2425 deletions

View file

@ -49,6 +49,14 @@ _v6.1.6_
- Fix webapp not reporting the correct version. - Fix webapp not reporting the correct version.
_v6.1.7_
- More concurrency mutexes to resolve SQL_BUSY errors.
- Better internal management of dirwatches.
- Fix SDRTrunk files not being ingested (discussion #108).
- Fix Trunk Recorder talkgroup_tag assign to the wrong property (issue #115).
- Improved the way the talkgroup label and name are autopopulated. If Trunk Recorder sends a talkgroup_tag with an empty value or with a single `-`, it will not overwrite the talkgroup label.
## Version 6.0 ## Version 6.0
- Backend server rewritten in Go for better performance and ease of installation. - Backend server rewritten in Go for better performance and ease of installation.

View file

@ -24,13 +24,13 @@ Clone the official repository on your computer and start the build process.
When finished, you will find the precompiled versions for various platforms in the `dist` folder. When finished, you will find the precompiled versions for various platforms in the `dist` folder.
rdio-scanner-darwin-amd64-v6.1.6.zip rdio-scanner-darwin-amd64-v6.1.7.zip
rdio-scanner-darwin-arm64-v6.1.6.zip rdio-scanner-darwin-arm64-v6.1.7.zip
rdio-scanner-freebsd-amd64-v6.1.6.zip rdio-scanner-freebsd-amd64-v6.1.7.zip
rdio-scanner-linux-386-v6.1.6.zip rdio-scanner-linux-386-v6.1.7.zip
rdio-scanner-linux-amd64-v6.1.6.zip rdio-scanner-linux-amd64-v6.1.7.zip
rdio-scanner-linux-arm64-v6.1.6.zip rdio-scanner-linux-arm64-v6.1.7.zip
rdio-scanner-linux-arm-v6.1.6.zip rdio-scanner-linux-arm-v6.1.7.zip
rdio-scanner-windows-amd64-v6.1.6.zip rdio-scanner-windows-amd64-v6.1.7.zip
**Happy Rdio scanning !** **Happy Rdio scanning !**

View file

@ -16,8 +16,8 @@
################################################################################ ################################################################################
app := rdio-scanner app := rdio-scanner
date := 2022/01/24 date := 2022/01/27
ver := 6.1.6 ver := 6.1.7
client := $(wildcard client/*.json client/*.ts) client := $(wildcard client/*.json client/*.ts)
server := $(wildcard server/*.go) server := $(wildcard server/*.go)

2205
client/package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -25,7 +25,6 @@
"@typescript-eslint/parser": "^5.10.0", "@typescript-eslint/parser": "^5.10.0",
"ajv-keywords": "^5.1.0", "ajv-keywords": "^5.1.0",
"eslint": "^8.7.0", "eslint": "^8.7.0",
"npm-check-updates": "^12.2.1",
"ts-node": "~10.4.0", "ts-node": "~10.4.0",
"typescript": "~4.4.4" "typescript": "~4.4.4"
}, },
@ -39,5 +38,5 @@
"build": "ng build --base-href ./ --configuration production", "build": "ng build --base-href ./ --configuration production",
"start": "ng serve" "start": "ng serve"
}, },
"version": "6.1.6" "version": "6.1.7"
} }

View file

@ -470,7 +470,7 @@ export class RdioScannerMainComponent implements OnDestroy, OnInit {
this.callFrequency = typeof this.call.frequency === 'number' this.callFrequency = typeof this.call.frequency === 'number'
? this.formatFrequency(this.call.frequency) ? this.formatFrequency(this.call.frequency)
: this.call.audioName?.replace(/\..+$/, '') || ''; : '';
this.callSpike = ''; this.callSpike = '';
} }

View file

@ -42,8 +42,8 @@ ALWAYS DOWNLOAD THE LATEST VERSION OF [RDIO SCANNER](https://github.com/chuot/rd
rdio@macos ~ % mkdir rdio-scanner rdio@macos ~ % mkdir rdio-scanner
rdio@macos ~ % cd rdio-scanner rdio@macos ~ % cd rdio-scanner
rdio@macos rdio-scanner % unzip \ rdio@macos rdio-scanner % unzip \
> ~/Downloads/rdio-scanner-darwin-arm64-v6.1.6.zip > ~/Downloads/rdio-scanner-darwin-arm64-v6.1.7.zip
Archive: /Users/rdio/Downloads/rdio-scanner-darwin-arm64-v6.1.6.zip Archive: /Users/rdio/Downloads/rdio-scanner-darwin-arm64-v6.1.7.zip
inflating: rdio-scanner inflating: rdio-scanner
inflating: rdio-scanner.pdf inflating: rdio-scanner.pdf
@ -51,11 +51,11 @@ ALWAYS DOWNLOAD THE LATEST VERSION OF [RDIO SCANNER](https://github.com/chuot/rd
rdio@macos rdio-scanner % ./rdio-scanner rdio@macos rdio-scanner % ./rdio-scanner
Rdio Scanner v6.1.6 Rdio Scanner v6.1.7
---------------------------------- ----------------------------------
2022/01/24 08:38:06 server started 2022/01/27 08:38:06 server started
2022/01/24 08:38:06 main interface at http://macos.local:3000 2022/01/27 08:38:06 main interface at http://macos.local:3000
2022/01/24 08:38:06 admin interface at http://macos.local:3000/admin 2022/01/27 08:38:06 admin interface at http://macos.local:3000/admin
4. Access the administrative dashboard to finalize the configuration. 4. Access the administrative dashboard to finalize the configuration.
@ -77,11 +77,11 @@ Here we want our [Rdio Scanner](https://guthub.com/chuot/rdio-scanner) instance
rdio@macos rdio-scanner % ./rdio-scanner --listen :80 rdio@macos rdio-scanner % ./rdio-scanner --listen :80
Rdio Scanner v6.1.6 Rdio Scanner v6.1.7
---------------------------------- ----------------------------------
2022/01/24 08:48:03 server started 2022/01/27 08:48:03 server started
2022/01/24 08:48:03 main interface at http://macos.local 2022/01/27 08:48:03 main interface at http://macos.local
2022/01/24 08:48:03 admin interface at http://macos.local/admin 2022/01/27 08:48:03 admin interface at http://macos.local/admin
## Listening with SSL with self-signed certificates ## Listening with SSL with self-signed certificates
@ -90,8 +90,8 @@ It is advised to share your [Rdio Scanner](https://github.com/chuot/rdio-scanner
We can create such self-signed certificates by running [Rdio Scanner](https://github.com/chuot/rdio-scanner) with the **-ssl_create** argument. We can create such self-signed certificates by running [Rdio Scanner](https://github.com/chuot/rdio-scanner) with the **-ssl_create** argument.
rdio@macos rdio-scanner % ./rdio-scanner -ssl_create rdio@macos rdio-scanner % ./rdio-scanner -ssl_create
2022/01/24 08:49:16 generating ssl certificate files 2022/01/27 08:49:16 generating ssl certificate files
2022/01/24 08:49:17 ssl files created 2022/01/27 08:49:17 ssl files created
This has generated these files in the same folder as your [Rdio Scanner](https://github.com/chuot/rdio-scanner) executable. This has generated these files in the same folder as your [Rdio Scanner](https://github.com/chuot/rdio-scanner) executable.
@ -108,12 +108,12 @@ Now we use the self-signed server certificate to run our instance.
> -ssl_key_file server.key \ > -ssl_key_file server.key \
> -ssl_listen :443 > -ssl_listen :443
Rdio Scanner v6.1.6 Rdio Scanner v6.1.7
---------------------------------- ----------------------------------
2022/01/24 08:50:58 server started 2022/01/27 08:50:58 server started
2022/01/24 08:50:58 main interface at http://macos.local 2022/01/27 08:50:58 main interface at http://macos.local
2022/01/24 08:50:58 main interface at https://macos.local 2022/01/27 08:50:58 main interface at https://macos.local
2022/01/24 08:50:58 admin interface at https://macos.local/admin 2022/01/27 08:50:58 admin interface at https://macos.local/admin
## Save your advanced configuration to a config file ## Save your advanced configuration to a config file
@ -125,7 +125,7 @@ You don't want to have to type everytime a long list of arguments. No problem, y
> -ssl_key_file server.key \ > -ssl_key_file server.key \
> -ssl_listen :443 \ > -ssl_listen :443 \
> -config_save > -config_save
2022/01/24 08:52:24 rdio-scanner.ini file created 2022/01/27 08:52:24 rdio-scanner.ini file created
All of your parameters passed as arguments to [Rdio Scanner](https://github.com/chuot/rdio-scanner) have been saved to an INI file which has the same arguments/values list. All of your parameters passed as arguments to [Rdio Scanner](https://github.com/chuot/rdio-scanner) have been saved to an INI file which has the same arguments/values list.
@ -140,12 +140,12 @@ Then simply run [Rdio Scanner](https://github.com/chuot/rdio-scanner) without an
rdio@macos rdio-scanner % ./rdio-scanner rdio@macos rdio-scanner % ./rdio-scanner
Rdio Scanner v6.1.6 Rdio Scanner v6.1.7
---------------------------------- ----------------------------------
2022/01/24 08:54:08 server started 2022/01/27 08:54:08 server started
2022/01/24 08:54:08 main interface at http://macos.local 2022/01/27 08:54:08 main interface at http://macos.local
2022/01/24 08:54:08 main interface at https://macos.local 2022/01/27 08:54:08 main interface at https://macos.local
2022/01/24 08:54:08 admin interface at https://macos.local/admin 2022/01/27 08:54:08 admin interface at https://macos.local/admin
## Install Rdio Scanner as a service ## Install Rdio Scanner as a service

View file

@ -42,8 +42,8 @@ ALWAYS DOWNLOAD THE LATEST VERSION OF [RDIO SCANNER](https://github.com/chuot/rd
rdio@pc-freebsd:~ $ mkdir rdio-scanner rdio@pc-freebsd:~ $ mkdir rdio-scanner
rdio@pc-freebsd:~ $ cd rdio-scanner rdio@pc-freebsd:~ $ cd rdio-scanner
rdio@pc-freebsd:~/rdio-scanner $ unzip \ rdio@pc-freebsd:~/rdio-scanner $ unzip \
> ~/rdio-scanner-freebsd-amd64-v6.1.6.zip > ~/rdio-scanner-freebsd-amd64-v6.1.7.zip
Archive: ../rdio-scanner-freebsd-amd64-v6.1.6.zip Archive: ../rdio-scanner-freebsd-amd64-v6.1.7.zip
extracting: rdio-scanner extracting: rdio-scanner
extracting: rdio-scanner.pdf extracting: rdio-scanner.pdf
@ -51,11 +51,11 @@ ALWAYS DOWNLOAD THE LATEST VERSION OF [RDIO SCANNER](https://github.com/chuot/rd
rdio@pc-freebsd:~/rdio-scanner $ ./rdio-scanner rdio@pc-freebsd:~/rdio-scanner $ ./rdio-scanner
Rdio Scanner v6.1.6 Rdio Scanner v6.1.7
---------------------------------- ----------------------------------
2022/01/24 08:16:36 server started 2022/01/27 08:16:36 server started
2022/01/24 08:16:36 main interface at http://pc-freebsd:3000 2022/01/27 08:16:36 main interface at http://pc-freebsd:3000
2022/01/24 08:16:36 admin interface at http://pc-freebsd:3000/admin 2022/01/27 08:16:36 admin interface at http://pc-freebsd:3000/admin
4. Access the administrative dashboard to finalize the configuration. 4. Access the administrative dashboard to finalize the configuration.
@ -81,11 +81,11 @@ Here we want our [Rdio Scanner](https://guthub.com/chuot/rdio-scanner) instance
Password: Password:
root@pc-freebsd:/home/rdio/rdio-scanner # ./rdio-scanner -listen :80 root@pc-freebsd:/home/rdio/rdio-scanner # ./rdio-scanner -listen :80
Rdio Scanner v6.1.6 Rdio Scanner v6.1.7
---------------------------------- ----------------------------------
2022/01/24 08:19:38 server started 2022/01/27 08:19:38 server started
2022/01/24 08:19:38 main interface at http://pc-freebsd 2022/01/27 08:19:38 main interface at http://pc-freebsd
2022/01/24 08:19:38 admin interface at http://pc-freebsd/admin 2022/01/27 08:19:38 admin interface at http://pc-freebsd/admin
## Listening with SSL with self-signed certificates ## Listening with SSL with self-signed certificates
@ -94,8 +94,8 @@ It is advised to share your [Rdio Scanner](https://github.com/chuot/rdio-scanner
We can create such self-signed certificates by running [Rdio Scanner](https://github.com/chuot/rdio-scanner) with the **-ssl_create** argument. We can create such self-signed certificates by running [Rdio Scanner](https://github.com/chuot/rdio-scanner) with the **-ssl_create** argument.
rdio@pc-freebsd:~/rdio-scanner $ ./rdio-scanner -ssl_create rdio@pc-freebsd:~/rdio-scanner $ ./rdio-scanner -ssl_create
2022/01/24 10:32:17 generating ssl certificate files 2022/01/27 10:32:17 generating ssl certificate files
2022/01/24 10:32:19 ssl files created 2022/01/27 10:32:19 ssl files created
This has generated these files in the same folder as your [Rdio Scanner](https://github.com/chuot/rdio-scanner) executable. This has generated these files in the same folder as your [Rdio Scanner](https://github.com/chuot/rdio-scanner) executable.
@ -114,12 +114,12 @@ Now we use the self-signed server certificate to run our instance.
> -ssl_key_file server.key \ > -ssl_key_file server.key \
> -ssl_listen :443 > -ssl_listen :443
Rdio Scanner v6.1.6 Rdio Scanner v6.1.7
---------------------------------- ----------------------------------
2022/01/24 10:34:29 server started 2022/01/27 10:34:29 server started
2022/01/24 10:34:29 main interface at http://pc-freebsd 2022/01/27 10:34:29 main interface at http://pc-freebsd
2022/01/24 10:34:29 main interface at https://pc-freebsd 2022/01/27 10:34:29 main interface at https://pc-freebsd
2022/01/24 10:34:29 admin interface at https://pc-freebsd/admin 2022/01/27 10:34:29 admin interface at https://pc-freebsd/admin
## Save your advanced configuration to a config file ## Save your advanced configuration to a config file
@ -131,7 +131,7 @@ You don't want to have to type everytime a long list of arguments. No problem, y
> -ssl_key_file server.key \ > -ssl_key_file server.key \
> -ssl_listen :443 \ > -ssl_listen :443 \
> -config_save > -config_save
2022/01/24 10:37:00 rdio-scanner.ini file created 2022/01/27 10:37:00 rdio-scanner.ini file created
All of your parameters passed as arguments to [Rdio Scanner](https://github.com/chuot/rdio-scanner) have been saved to an INI file which has the same arguments/values list. All of your parameters passed as arguments to [Rdio Scanner](https://github.com/chuot/rdio-scanner) have been saved to an INI file which has the same arguments/values list.
@ -148,12 +148,12 @@ Then simply run [Rdio Scanner](https://github.com/chuot/rdio-scanner) without an
Password: Password:
root@pc-freebsd:/home/rdio/rdio-scanner # ./rdio-scanner root@pc-freebsd:/home/rdio/rdio-scanner # ./rdio-scanner
Rdio Scanner v6.1.6 Rdio Scanner v6.1.7
---------------------------------- ----------------------------------
2022/01/24 10:38:28 server started 2022/01/27 10:38:28 server started
2022/01/24 10:38:29 main interface at http://pc-freebsd 2022/01/27 10:38:29 main interface at http://pc-freebsd
2022/01/24 10:38:29 main interface at https://pc-freebsd 2022/01/27 10:38:29 main interface at https://pc-freebsd
2022/01/24 10:38:29 admin interface at https://pc-freebsd/admin 2022/01/27 10:38:29 admin interface at https://pc-freebsd/admin
## Install Rdio Scanner as a service ## Install Rdio Scanner as a service

View file

@ -42,8 +42,8 @@ ALWAYS DOWNLOAD THE LATEST VERSION OF [RDIO SCANNER](https://github.com/chuot/rd
[rdio@pc-linux ~]$ mkdir rdio-scanner [rdio@pc-linux ~]$ mkdir rdio-scanner
[rdio@pc-linux ~]$ cd rdio-scanner [rdio@pc-linux ~]$ cd rdio-scanner
[rdio@pc-linux rdio-scanner]$ unzip \ [rdio@pc-linux rdio-scanner]$ unzip \
> ~/Downloads/rdio-scanner-linux-amd64-v6.1.6.zip > ~/Downloads/rdio-scanner-linux-amd64-v6.1.7.zip
Archive: /home/rdio/Downloads/rdio-scanner-linux-amd64-v6.1.6.zip Archive: /home/rdio/Downloads/rdio-scanner-linux-amd64-v6.1.7.zip
inflating: rdio-scanner inflating: rdio-scanner
inflating: rdio-scanner.pdf inflating: rdio-scanner.pdf
@ -51,11 +51,11 @@ ALWAYS DOWNLOAD THE LATEST VERSION OF [RDIO SCANNER](https://github.com/chuot/rd
[rdio@pc-linux rdio-scanner]$ ./rdio-scanner [rdio@pc-linux rdio-scanner]$ ./rdio-scanner
Rdio Scanner v6.1.6 Rdio Scanner v6.1.7
---------------------------------- ----------------------------------
2022/01/24 09:11:48 server started 2022/01/27 09:11:48 server started
2022/01/24 09:11:48 main interface at http://pc-linux:3000 2022/01/27 09:11:48 main interface at http://pc-linux:3000
2022/01/24 09:11:48 admin interface at http://pc-linux:3000/admin 2022/01/27 09:11:48 admin interface at http://pc-linux:3000/admin
4. Access the administrative dashboard to finalize the configuration. 4. Access the administrative dashboard to finalize the configuration.
@ -78,11 +78,11 @@ Here we want our [Rdio Scanner](https://guthub.com/chuot/rdio-scanner) instance
[rdio@pc-linux rdio-scanner]$ sudo ./rdio-scanner -listen :80 [rdio@pc-linux rdio-scanner]$ sudo ./rdio-scanner -listen :80
[sudo] password for rdio: [sudo] password for rdio:
Rdio Scanner v6.1.6 Rdio Scanner v6.1.7
---------------------------------- ----------------------------------
2022/01/24 09:14:00 server started 2022/01/27 09:14:00 server started
2022/01/24 09:14:00 main interface at http://pc-linux 2022/01/27 09:14:00 main interface at http://pc-linux
2022/01/24 09:14:00 admin interface at http://pc-linux/admin 2022/01/27 09:14:00 admin interface at http://pc-linux/admin
## Listening with SSL with self-signed certificates ## Listening with SSL with self-signed certificates
@ -91,8 +91,8 @@ It is advised to share your [Rdio Scanner](https://github.com/chuot/rdio-scanner
We can create such self-signed certificates by running [Rdio Scanner](https://github.com/chuot/rdio-scanner) with the **-ssl_create** argument. We can create such self-signed certificates by running [Rdio Scanner](https://github.com/chuot/rdio-scanner) with the **-ssl_create** argument.
[rdio@pc-linux rdio-scanner]$ ./rdio-scanner -ssl_create [rdio@pc-linux rdio-scanner]$ ./rdio-scanner -ssl_create
2022/01/24 09:15:03 generating ssl certificate files 2022/01/27 09:15:03 generating ssl certificate files
2022/01/24 09:15:05 ssl files created 2022/01/27 09:15:05 ssl files created
This has generated these files in the same folder as your [Rdio Scanner](https://github.com/chuot/rdio-scanner) executable. This has generated these files in the same folder as your [Rdio Scanner](https://github.com/chuot/rdio-scanner) executable.
@ -110,12 +110,12 @@ Now we use the self-signed server certificate to run our instance.
> -ssl_listen :443 > -ssl_listen :443
[sudo] password for rdio: [sudo] password for rdio:
Rdio Scanner v6.1.6 Rdio Scanner v6.1.7
---------------------------------- ----------------------------------
2022/01/24 09:16:47 server started 2022/01/27 09:16:47 server started
2022/01/24 09:16:47 main interface at http://pc-linux 2022/01/27 09:16:47 main interface at http://pc-linux
2022/01/24 09:16:47 main interface at https://pc-linux 2022/01/27 09:16:47 main interface at https://pc-linux
2022/01/24 09:16:47 admin interface at https://pc-linux/admin 2022/01/27 09:16:47 admin interface at https://pc-linux/admin
## Save your advanced configuration to a config file ## Save your advanced configuration to a config file
@ -127,7 +127,7 @@ You don't want to have to type everytime a long list of arguments. No problem, y
> -ssl_key_file server.key \ > -ssl_key_file server.key \
> -ssl_listen :443 \ > -ssl_listen :443 \
> -config_save > -config_save
2022/01/24 09:19:29 rdio-scanner.ini file created 2022/01/27 09:19:29 rdio-scanner.ini file created
All of your parameters passed as arguments to [Rdio Scanner](https://github.com/chuot/rdio-scanner) have been saved to an INI file which has the same arguments/values list. All of your parameters passed as arguments to [Rdio Scanner](https://github.com/chuot/rdio-scanner) have been saved to an INI file which has the same arguments/values list.
@ -143,12 +143,12 @@ Then simply run [Rdio Scanner](https://github.com/chuot/rdio-scanner) without an
[rdio@pc-linux rdio-scanner]$ sudo ./rdio-scanner [rdio@pc-linux rdio-scanner]$ sudo ./rdio-scanner
[sudo] Mot de passe de rdio : [sudo] Mot de passe de rdio :
Rdio Scanner v6.1.6 Rdio Scanner v6.1.7
---------------------------------- ----------------------------------
2022/01/24 09:20:40 server started 2022/01/27 09:20:40 server started
2022/01/24 09:20:40 main interface at http://pc-linux 2022/01/27 09:20:40 main interface at http://pc-linux
2022/01/24 09:20:40 main interface at https://pc-linux 2022/01/27 09:20:40 main interface at https://pc-linux
2022/01/24 09:20:40 admin interface at https://pc-linux/admin 2022/01/27 09:20:40 admin interface at https://pc-linux/admin
## Install Rdio Scanner as a service ## Install Rdio Scanner as a service

View file

@ -44,7 +44,7 @@ ALWAYS DOWNLOAD THE LATEST VERSION OF [RDIO SCANNER](https://github.com/chuot/rd
C:\Users\rdio> cd rdio-scanner C:\Users\rdio> cd rdio-scanner
C:\Users\rdio\rdio-scanner> tar -xvf ^ C:\Users\rdio\rdio-scanner> tar -xvf ^
..\downloads\rdio-scanner-windows-amd64-v6.1.6.zip ..\downloads\rdio-scanner-windows-amd64-v6.1.7.zip
x rdio-scanner.exe x rdio-scanner.exe
x rdio-scanner.pdf x rdio-scanner.pdf
@ -52,11 +52,11 @@ ALWAYS DOWNLOAD THE LATEST VERSION OF [RDIO SCANNER](https://github.com/chuot/rd
C:\Users\rdio\rdio-scanner>rdio-scanner C:\Users\rdio\rdio-scanner>rdio-scanner
Rdio Scanner v6.1.6 Rdio Scanner v6.1.7
---------------------------------- ----------------------------------
2022/01/24 13:48:48 server started 2022/01/27 13:48:48 server started
2022/01/24 13:48:48 main interface at http://pc-windows:3000 2022/01/27 13:48:48 main interface at http://pc-windows:3000
2022/01/24 13:48:48 admin interface at http://pc-windows:3000/admin 2022/01/27 13:48:48 admin interface at http://pc-windows:3000/admin
4. Access the administrative dashboard to finalize the configuration. 4. Access the administrative dashboard to finalize the configuration.
@ -78,11 +78,11 @@ Here we want our [Rdio Scanner](https://guthub.com/chuot/rdio-scanner) instance
C:\Users\rdio\rdio-scanner>rdio-scanner -listen :80 C:\Users\rdio\rdio-scanner>rdio-scanner -listen :80
Rdio Scanner v6.1.6 Rdio Scanner v6.1.7
---------------------------------- ----------------------------------
2022/01/24 10:53:31 server started 2022/01/27 10:53:31 server started
2022/01/24 10:53:31 main interface at http://pc-windows 2022/01/27 10:53:31 main interface at http://pc-windows
2022/01/24 10:53:31 admin interface at http://pc-windows/admin 2022/01/27 10:53:31 admin interface at http://pc-windows/admin
## Listening with SSL with self-signed certificates ## Listening with SSL with self-signed certificates
@ -91,8 +91,8 @@ It is advised to share your [Rdio Scanner](https://github.com/chuot/rdio-scanner
We can create such self-signed certificates by running [Rdio Scanner](https://github.com/chuot/rdio-scanner) with the **-ssl_create** argument. We can create such self-signed certificates by running [Rdio Scanner](https://github.com/chuot/rdio-scanner) with the **-ssl_create** argument.
C:\Users\rdio\rdio-scanner>rdio-scanner -ssl_create C:\Users\rdio\rdio-scanner>rdio-scanner -ssl_create
2022/01/24 10:58:51 generating ssl certificate files 2022/01/27 10:58:51 generating ssl certificate files
2022/01/24 10:58:54 ssl files created 2022/01/27 10:58:54 ssl files created
This has generated these files in the same folder as your [Rdio Scanner](https://github.com/chuot/rdio-scanner) executable. This has generated these files in the same folder as your [Rdio Scanner](https://github.com/chuot/rdio-scanner) executable.
@ -109,12 +109,12 @@ Now we use the self-signed server certificate to run our instance.
-ssl_key_file server.key ^ -ssl_key_file server.key ^
-ssl_listen :443 -ssl_listen :443
Rdio Scanner v6.1.6 Rdio Scanner v6.1.7
---------------------------------- ----------------------------------
2022/01/24 11:05:43 server started 2022/01/27 11:05:43 server started
2022/01/24 11:05:43 main interface at http://pc-windows 2022/01/27 11:05:43 main interface at http://pc-windows
2022/01/24 11:05:43 main interface at https://pc-windows 2022/01/27 11:05:43 main interface at https://pc-windows
2022/01/24 11:05:43 admin interface at https://pc-windows/admin 2022/01/27 11:05:43 admin interface at https://pc-windows/admin
## Save your advanced configuration to a config file ## Save your advanced configuration to a config file
@ -126,7 +126,7 @@ You don't want to have to type everytime a long list of arguments. No problem, y
-ssl_key_file server.key ^ -ssl_key_file server.key ^
-ssl_listen :443 ^ -ssl_listen :443 ^
-config_save -config_save
2022/01/24 11:08:28 rdio-scanner.ini file created 2022/01/27 11:08:28 rdio-scanner.ini file created
All of your parameters passed as arguments to [Rdio Scanner](https://github.com/chuot/rdio-scanner) have been saved to an INI file which has the same arguments/values list. All of your parameters passed as arguments to [Rdio Scanner](https://github.com/chuot/rdio-scanner) have been saved to an INI file which has the same arguments/values list.
@ -141,12 +141,12 @@ Then simply run [Rdio Scanner](https://github.com/chuot/rdio-scanner) without an
C:\Users\rdio\rdio-scanner>rdio-scanner C:\Users\rdio\rdio-scanner>rdio-scanner
Rdio Scanner v6.1.6 Rdio Scanner v6.1.7
---------------------------------- ----------------------------------
2022/01/24 11:11:28 server started 2022/01/27 11:11:28 server started
2022/01/24 11:11:28 main interface at http://pc-windows 2022/01/27 11:11:28 main interface at http://pc-windows
2022/01/24 11:11:28 main interface at https://pc-windows 2022/01/27 11:11:28 main interface at https://pc-windows
2022/01/24 11:11:28 admin interface at https://pc-windows/admin 2022/01/27 11:11:28 admin interface at https://pc-windows/admin
\pagebreak{} \pagebreak{}

6
package-lock.json generated Normal file
View file

@ -0,0 +1,6 @@
{
"name": "rdio-scanner",
"lockfileVersion": 2,
"requires": true,
"packages": {}
}

1
package.json Normal file
View file

@ -0,0 +1 @@
{}

View file

@ -127,13 +127,13 @@ func (access *Access) HasExpired() bool {
type Accesses struct { type Accesses struct {
List []*Access List []*Access
mutex sync.RWMutex mutex sync.Mutex
} }
func NewAccesses() *Accesses { func NewAccesses() *Accesses {
return &Accesses{ return &Accesses{
List: []*Access{}, List: []*Access{},
mutex: sync.RWMutex{}, mutex: sync.Mutex{},
} }
} }
@ -154,8 +154,8 @@ func (accesses *Accesses) FromMap(f []interface{}) {
} }
func (accesses *Accesses) GetAccess(code string) (access *Access, ok bool) { func (accesses *Accesses) GetAccess(code string) (access *Access, ok bool) {
accesses.mutex.RLock() accesses.mutex.Lock()
defer accesses.mutex.RUnlock() defer accesses.mutex.Unlock()
for _, access := range accesses.List { for _, access := range accesses.List {
if access.Code == code { if access.Code == code {
@ -167,8 +167,8 @@ func (accesses *Accesses) GetAccess(code string) (access *Access, ok bool) {
} }
func (accesses *Accesses) IsRestricted() bool { func (accesses *Accesses) IsRestricted() bool {
accesses.mutex.RLock() accesses.mutex.Lock()
defer accesses.mutex.RUnlock() defer accesses.mutex.Unlock()
return len(accesses.List) > 0 return len(accesses.List) > 0
} }
@ -185,8 +185,8 @@ func (accesses *Accesses) Read(db *Database) error {
t time.Time t time.Time
) )
accesses.mutex.RLock() accesses.mutex.Lock()
defer accesses.mutex.RUnlock() defer accesses.mutex.Unlock()
accesses.List = []*Access{} accesses.List = []*Access{}

View file

@ -22,6 +22,7 @@ import (
"net/http" "net/http"
"os" "os"
"strings" "strings"
"sync"
"time" "time"
"github.com/golang-jwt/jwt/v4" "github.com/golang-jwt/jwt/v4"
@ -47,6 +48,7 @@ type Admin struct {
Register chan *websocket.Conn Register chan *websocket.Conn
Tokens []string Tokens []string
Unregister chan *websocket.Conn Unregister chan *websocket.Conn
mutex sync.Mutex
running bool running bool
} }
@ -68,6 +70,7 @@ func NewAdmin(controller *Controller) *Admin {
Register: make(chan *websocket.Conn, 100), Register: make(chan *websocket.Conn, 100),
Tokens: []string{}, Tokens: []string{},
Unregister: make(chan *websocket.Conn, 100), Unregister: make(chan *websocket.Conn, 100),
mutex: sync.Mutex{},
} }
} }
@ -145,6 +148,11 @@ func (admin *Admin) ConfigHandler(w http.ResponseWriter, r *http.Request) {
}() }()
} else { } else {
admin.Controller.Calls.mutex.Lock()
admin.mutex.Lock()
defer admin.mutex.Unlock()
defer admin.Controller.Calls.mutex.Unlock()
logError := func(err error) { logError := func(err error) {
admin.Controller.Logs.LogEvent(admin.Controller.Database, LogLevelError, fmt.Sprintf("admin.confighandler.put: %s", err.Error())) admin.Controller.Logs.LogEvent(admin.Controller.Database, LogLevelError, fmt.Sprintf("admin.confighandler.put: %s", err.Error()))
} }

View file

@ -110,13 +110,13 @@ func (apikey *Apikey) HasAccess(call *Call) bool {
type Apikeys struct { type Apikeys struct {
List []*Apikey List []*Apikey
mutex sync.RWMutex mutex sync.Mutex
} }
func NewApikeys() *Apikeys { func NewApikeys() *Apikeys {
return &Apikeys{ return &Apikeys{
List: []*Apikey{}, List: []*Apikey{},
mutex: sync.RWMutex{}, mutex: sync.Mutex{},
} }
} }
@ -137,8 +137,8 @@ func (apikeys *Apikeys) FromMap(f []interface{}) {
} }
func (apikeys *Apikeys) GetApikey(key string) (apikey *Apikey, ok bool) { func (apikeys *Apikeys) GetApikey(key string) (apikey *Apikey, ok bool) {
apikeys.mutex.RLock() apikeys.mutex.Lock()
defer apikeys.mutex.RUnlock() defer apikeys.mutex.Unlock()
for _, apikey := range apikeys.List { for _, apikey := range apikeys.List {
if apikey.Key == key && !apikey.Disabled { if apikey.Key == key && !apikey.Disabled {
@ -157,8 +157,8 @@ func (apikeys *Apikeys) Read(db *Database) error {
systems string systems string
) )
apikeys.mutex.RLock() apikeys.mutex.Lock()
defer apikeys.mutex.RUnlock() defer apikeys.mutex.Unlock()
apikeys.List = []*Apikey{} apikeys.List = []*Apikey{}

View file

@ -106,20 +106,20 @@ func (call *Call) ToJson() (string, error) {
} }
type Calls struct { type Calls struct {
mutex sync.RWMutex mutex sync.Mutex
} }
func NewCalls() *Calls { func NewCalls() *Calls {
return &Calls{ return &Calls{
mutex: sync.RWMutex{}, mutex: sync.Mutex{},
} }
} }
func (calls *Calls) CheckDuplicate(call *Call, msTimeFrame uint, db *Database) bool { func (calls *Calls) CheckDuplicate(call *Call, msTimeFrame uint, db *Database) bool {
var count uint var count uint
calls.mutex.RLock() calls.mutex.Lock()
defer calls.mutex.RUnlock() defer calls.mutex.Unlock()
d := time.Duration(msTimeFrame) * time.Millisecond d := time.Duration(msTimeFrame) * time.Millisecond
from := call.DateTime.Add(-d).Format(db.DateTimeFormat) from := call.DateTime.Add(-d).Format(db.DateTimeFormat)
@ -142,8 +142,8 @@ func (calls *Calls) GetCall(id uint, db *Database) (*Call, error) {
t time.Time t time.Time
) )
calls.mutex.RLock() calls.mutex.Lock()
defer calls.mutex.RUnlock() defer calls.mutex.Unlock()
call := Call{} call := Call{}
@ -209,8 +209,8 @@ func (calls *Calls) Search(searchOptions *CallsSearchOptions, client *Client) (*
where string = "true" where string = "true"
) )
calls.mutex.RLock() calls.mutex.Lock()
defer calls.mutex.RUnlock() defer calls.mutex.Unlock()
db := client.Controller.Database db := client.Controller.Database

View file

@ -209,7 +209,7 @@ func (config *Config) GetPath(p string) string {
if path.IsAbs(p) { if path.IsAbs(p) {
return p return p
} }
return path.Join(config.BaseDir, p) return filepath.Join(config.BaseDir, p)
} }
func (config *Config) GetSslCaCertFilePath() string { func (config *Config) GetSslCaCertFilePath() string {

View file

@ -81,10 +81,6 @@ func NewController(config *Config) *Controller {
return controller return controller
} }
func (controller *Controller) CheckDuplicate(call *Call) bool {
return controller.Calls.CheckDuplicate(call, controller.Options.DuplicateDetectionTimeFrame, controller.Database)
}
func (controller *Controller) ConvertAudio(call *Call) { func (controller *Controller) ConvertAudio(call *Call) {
var ( var (
args = []string{"-i", "-"} args = []string{"-i", "-"}
@ -295,7 +291,6 @@ func (controller *Controller) IngestCall(call *Call) {
GroupId: groupId, GroupId: groupId,
Id: call.Talkgroup, Id: call.Talkgroup,
Label: fmt.Sprintf("%d", call.Talkgroup), Label: fmt.Sprintf("%d", call.Talkgroup),
Name: fmt.Sprintf("Talkgroup %d", call.Talkgroup),
TagId: tagId, TagId: tagId,
} }
@ -316,6 +311,11 @@ func (controller *Controller) IngestCall(call *Call) {
populated = true populated = true
talkgroup.Name = v talkgroup.Name = v
} }
default:
if len(talkgroup.Name) == 0 {
populated = true
talkgroup.Name = talkgroup.Label
}
} }
} }
@ -350,7 +350,7 @@ func (controller *Controller) IngestCall(call *Call) {
} }
if !controller.Options.DisableDuplicateDetection { if !controller.Options.DisableDuplicateDetection {
if controller.CheckDuplicate(call) { if controller.Calls.CheckDuplicate(call, controller.Options.DuplicateDetectionTimeFrame, controller.Database) {
logCall(call, LogLevelWarn, "duplicate call rejected") logCall(call, LogLevelWarn, "duplicate call rejected")
return return
} }

View file

@ -61,9 +61,10 @@ func NewDatabase(config *Config) *Database {
log.Fatalf("unknown database type %s\n", config.DbType) log.Fatalf("unknown database type %s\n", config.DbType)
} }
database.Sql.SetConnMaxIdleTime(time.Second * 5)
database.Sql.SetConnMaxLifetime(0) database.Sql.SetConnMaxLifetime(0)
database.Sql.SetMaxIdleConns(10) database.Sql.SetMaxIdleConns(64)
database.Sql.SetMaxOpenConns(10) database.Sql.SetMaxOpenConns(64)
if err = database.migrate(); err != nil { if err = database.migrate(); err != nil {
log.Fatal(err) log.Fatal(err)

View file

@ -85,6 +85,7 @@ var defaults Defaults = Defaults{
"Air", "Air",
"EMS", "EMS",
"Fire", "Fire",
"Interop",
"Law", "Law",
}, },
keypadBeeps: "uniden", keypadBeeps: "uniden",
@ -102,15 +103,13 @@ var defaults Defaults = Defaults{
}, },
systems: []System{}, systems: []System{},
tags: []string{ tags: []string{
"Air Apron", "Air Traffic Control",
"Air Ground", "Emergency ",
"Air Traffic",
"EMS Dispatch ",
"EMS Operations ",
"Fire Dispatch", "Fire Dispatch",
"Fire Tac", "Fire Tac",
"Fire Talk", "Fire Talk",
"Law Dispatch", "Interop",
"Law Talk", "Security",
"Service",
}, },
} }

View file

@ -25,6 +25,7 @@ import (
"mime" "mime"
"os" "os"
"path" "path"
"path/filepath"
"regexp" "regexp"
"sort" "sort"
"strconv" "strconv"
@ -57,6 +58,7 @@ type Dirwatch struct {
UsePolling bool `json:"usePolling"` UsePolling bool `json:"usePolling"`
controller *Controller controller *Controller
dirs map[string]bool dirs map[string]bool
pending map[string]*time.Timer
running bool running bool
watcher *fsnotify.Watcher watcher *fsnotify.Watcher
} }
@ -465,7 +467,11 @@ func (dirwatch *Dirwatch) Start(controller *Controller) error {
newTimer := func(eventName string) *time.Timer { newTimer := func(eventName string) *time.Timer {
return time.AfterFunc(2*time.Second, func() { return time.AfterFunc(2*time.Second, func() {
dirwatch.Ingest(eventName) if dirwatch.running {
if _, err := os.Stat(eventName); err == nil {
dirwatch.Ingest(eventName)
}
}
}) })
} }
@ -475,6 +481,7 @@ func (dirwatch *Dirwatch) Start(controller *Controller) error {
if dirwatch.watcher == nil { if dirwatch.watcher == nil {
break break
} }
if event, ok := <-dirwatch.watcher.Events; ok { if event, ok := <-dirwatch.watcher.Events; ok {
switch event.Op { switch event.Op {
case fsnotify.Create: case fsnotify.Create:
@ -482,11 +489,16 @@ func (dirwatch *Dirwatch) Start(controller *Controller) error {
case uint: case uint:
time.Sleep(time.Duration(v) * time.Millisecond) time.Sleep(time.Duration(v) * time.Millisecond)
} }
if dirwatch.isDir(event.Name) { if dirwatch.isDir(event.Name) {
if err := dirwatch.walkDir(event.Name); err != nil { if err := dirwatch.walkDir(event.Name); err != nil {
logError(err) logError(err)
} }
} else { } else {
if pending[event.Name] != nil {
pending[event.Name].Stop()
}
pending[event.Name] = newTimer(event.Name) pending[event.Name] = newTimer(event.Name)
} }
@ -501,16 +513,28 @@ func (dirwatch *Dirwatch) Start(controller *Controller) error {
case fsnotify.Write: case fsnotify.Write:
if pending[event.Name] != nil { if pending[event.Name] != nil {
pending[event.Name].Stop() if pending[event.Name] != nil {
pending[event.Name].Stop()
}
pending[event.Name] = newTimer(event.Name) pending[event.Name] = newTimer(event.Name)
} }
} }
} else if dirwatch.running { } else if dirwatch.running {
dirwatch.watcher.Close() if dirwatch.watcher != nil {
dirwatch.watcher, _ = fsnotify.NewWatcher() dirwatch.watcher.Close()
}
time.Sleep(2 * time.Second) time.Sleep(2 * time.Second)
if dirwatch.watcher, err = fsnotify.NewWatcher(); err != nil {
controller.Logs.LogEvent(
controller.Database,
LogLevelError,
fmt.Sprintf("dirwatch.watcher.restart: %s", err.Error()),
)
}
} else { } else {
dirwatch.Stop() dirwatch.Stop()
break break
@ -521,10 +545,12 @@ func (dirwatch *Dirwatch) Start(controller *Controller) error {
go watcher() go watcher()
go func() { go func() {
time.Sleep(2 * time.Second) if err := fs.WalkDir(os.DirFS(dirwatch.Directory), ".", func(p string, d fs.DirEntry, err error) error {
if !dirwatch.running {
return nil
}
fs.WalkDir(os.DirFS(dirwatch.Directory), ".", func(p string, d fs.DirEntry, err error) error { fp := filepath.Join(dirwatch.Directory, p)
fp := path.Join(dirwatch.Directory, p)
if dirwatch.isDir(fp) { if dirwatch.isDir(fp) {
dirwatch.dirs[fp] = true dirwatch.dirs[fp] = true
@ -533,31 +559,42 @@ func (dirwatch *Dirwatch) Start(controller *Controller) error {
} else if dirwatch.DeleteAfter { } else if dirwatch.DeleteAfter {
dirwatch.Ingest(fp) dirwatch.Ingest(fp)
} }
return err return err
}) }); err != nil {
controller.Logs.LogEvent(
controller.Database,
LogLevelError,
fmt.Sprintf("dirwatch.walkdir: %s", err.Error()),
)
}
}() }()
return nil return nil
} }
func (dirwatch *Dirwatch) Stop() { func (dirwatch *Dirwatch) Stop() {
dirwatch.running = false
for k := range dirwatch.pending {
dirwatch.pending[k].Stop()
}
if dirwatch.watcher != nil { if dirwatch.watcher != nil {
dirwatch.watcher.Close() dirwatch.watcher.Close()
dirwatch.watcher = nil dirwatch.watcher = nil
} }
dirwatch.running = false
} }
type Dirwatches struct { type Dirwatches struct {
List []*Dirwatch List []*Dirwatch
mutex sync.RWMutex mutex sync.Mutex
} }
func NewDirwatches() *Dirwatches { func NewDirwatches() *Dirwatches {
return &Dirwatches{ return &Dirwatches{
List: []*Dirwatch{}, List: []*Dirwatch{},
mutex: sync.RWMutex{}, mutex: sync.Mutex{},
} }
} }
@ -594,8 +631,8 @@ func (dirwatches *Dirwatches) Read(db *Database) error {
talkgroupId sql.NullFloat64 talkgroupId sql.NullFloat64
) )
dirwatches.mutex.RLock() dirwatches.mutex.Lock()
defer dirwatches.mutex.RUnlock() defer dirwatches.mutex.Unlock()
dirwatches.Stop() dirwatches.Stop()
@ -610,7 +647,7 @@ func (dirwatches *Dirwatches) Read(db *Database) error {
} }
for rows.Next() { for rows.Next() {
dirwatch := &Dirwatch{} dirwatch := &Dirwatch{pending: map[string]*time.Timer{}}
if err = rows.Scan(&id, &delay, &dirwatch.DeleteAfter, &dirwatch.Directory, &dirwatch.Disabled, &extension, &frequency, &mask, &order, &systemId, &talkgroupId, &kind, &dirwatch.UsePolling); err != nil { if err = rows.Scan(&id, &delay, &dirwatch.DeleteAfter, &dirwatch.Directory, &dirwatch.Disabled, &extension, &frequency, &mask, &order, &systemId, &talkgroupId, &kind, &dirwatch.UsePolling); err != nil {
break break
@ -770,7 +807,7 @@ func (dirwatch *Dirwatch) walkDir(d string) error {
dfs := os.DirFS(d) dfs := os.DirFS(d)
return fs.WalkDir(dfs, ".", func(p string, de fs.DirEntry, err error) error { return fs.WalkDir(dfs, ".", func(p string, de fs.DirEntry, err error) error {
fp := path.Join(d, p) fp := filepath.Join(d, p)
if dirwatch.isDir(fp) { if dirwatch.isDir(fp) {
if !dirwatch.dirs[fp] { if !dirwatch.dirs[fp] {
dirwatch.dirs[fp] = true dirwatch.dirs[fp] = true

View file

@ -350,13 +350,13 @@ func (downstream *Downstream) Send(call *Call) error {
type Downstreams struct { type Downstreams struct {
List []*Downstream List []*Downstream
mutex sync.RWMutex mutex sync.Mutex
} }
func NewDownstreams() *Downstreams { func NewDownstreams() *Downstreams {
return &Downstreams{ return &Downstreams{
List: []*Downstream{}, List: []*Downstream{},
mutex: sync.RWMutex{}, mutex: sync.Mutex{},
} }
} }
@ -384,8 +384,8 @@ func (downstreams *Downstreams) Read(db *Database) error {
rows *sql.Rows rows *sql.Rows
) )
downstreams.mutex.RLock() downstreams.mutex.Lock()
defer downstreams.mutex.RUnlock() defer downstreams.mutex.Unlock()
downstreams.List = []*Downstream{} downstreams.List = []*Downstream{}

View file

@ -28,8 +28,8 @@ require (
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
lukechampine.com/uint128 v1.2.0 // indirect lukechampine.com/uint128 v1.2.0 // indirect
modernc.org/cc/v3 v3.35.22 // indirect modernc.org/cc/v3 v3.35.22 // indirect
modernc.org/ccgo/v3 v3.15.11 // indirect modernc.org/ccgo/v3 v3.15.13 // indirect
modernc.org/libc v1.14.4 // indirect modernc.org/libc v1.14.5 // indirect
modernc.org/mathutil v1.4.1 // indirect modernc.org/mathutil v1.4.1 // indirect
modernc.org/memory v1.0.5 // indirect modernc.org/memory v1.0.5 // indirect
modernc.org/opt v0.1.1 // indirect modernc.org/opt v0.1.1 // indirect

View file

@ -152,10 +152,12 @@ modernc.org/ccgo/v3 v3.14.0/go.mod h1:hBrkiBlUwvr5vV/ZH9YzXIp982jKE8Ek8tR1ytoAL6
modernc.org/ccgo/v3 v3.15.1/go.mod h1:md59wBwDT2LznX/OTCPoVS6KIsdRgY8xqQwBV+hkTH0= modernc.org/ccgo/v3 v3.15.1/go.mod h1:md59wBwDT2LznX/OTCPoVS6KIsdRgY8xqQwBV+hkTH0=
modernc.org/ccgo/v3 v3.15.9/go.mod h1:md59wBwDT2LznX/OTCPoVS6KIsdRgY8xqQwBV+hkTH0= modernc.org/ccgo/v3 v3.15.9/go.mod h1:md59wBwDT2LznX/OTCPoVS6KIsdRgY8xqQwBV+hkTH0=
modernc.org/ccgo/v3 v3.15.10/go.mod h1:wQKxoFn0ynxMuCLfFD09c8XPUCc8obfchoVR9Cn0fI8= modernc.org/ccgo/v3 v3.15.10/go.mod h1:wQKxoFn0ynxMuCLfFD09c8XPUCc8obfchoVR9Cn0fI8=
modernc.org/ccgo/v3 v3.15.11 h1:rBUQkkX3u/Sh1ipCDCCwGnaPIjaErgtR23S5mHzuJH4= modernc.org/ccgo/v3 v3.15.12/go.mod h1:VFePOWoCd8uDGRJpq/zfJ29D0EVzMSyID8LCMWYbX6I=
modernc.org/ccgo/v3 v3.15.11/go.mod h1:VFePOWoCd8uDGRJpq/zfJ29D0EVzMSyID8LCMWYbX6I= modernc.org/ccgo/v3 v3.15.13 h1:hqlCzNJTXLrhS70y1PqWckrF9x1btSQRC7JFuQcBg5c=
modernc.org/ccorpus v1.11.1 h1:K0qPfpVG1MJh5BYazccnmhywH4zHuOgJXgbjzyp6dWA= modernc.org/ccgo/v3 v3.15.13/go.mod h1:QHtvdpeODlXjdK3tsbpyK+7U9JV4PQsrPGIbtmc0KfY=
modernc.org/ccorpus v1.11.1/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ= modernc.org/ccorpus v1.11.1/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ=
modernc.org/ccorpus v1.11.4 h1:YOmQBBzE8GC/puUx76D5j/gJYIZQsydrh6VMJVfXF0M=
modernc.org/ccorpus v1.11.4/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ=
modernc.org/httpfs v1.0.6 h1:AAgIpFZRXuYnkjftxTAZwMIiwEqAfk8aVB2/oA6nAeM= modernc.org/httpfs v1.0.6 h1:AAgIpFZRXuYnkjftxTAZwMIiwEqAfk8aVB2/oA6nAeM=
modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM= modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM=
modernc.org/libc v1.9.8/go.mod h1:U1eq8YWr/Kc1RWCMFUWEdkTg8OTcfLw2kY8EDwl039w= modernc.org/libc v1.9.8/go.mod h1:U1eq8YWr/Kc1RWCMFUWEdkTg8OTcfLw2kY8EDwl039w=
@ -201,8 +203,8 @@ modernc.org/libc v1.13.2/go.mod h1:npFeGWjmZTjFeWALQLrvklVmAxv4m80jnG3+xI8FdJk=
modernc.org/libc v1.14.1/go.mod h1:npFeGWjmZTjFeWALQLrvklVmAxv4m80jnG3+xI8FdJk= modernc.org/libc v1.14.1/go.mod h1:npFeGWjmZTjFeWALQLrvklVmAxv4m80jnG3+xI8FdJk=
modernc.org/libc v1.14.2/go.mod h1:MX1GBLnRLNdvmK9azU9LCxZ5lMyhrbEMK8rG3X/Fe34= modernc.org/libc v1.14.2/go.mod h1:MX1GBLnRLNdvmK9azU9LCxZ5lMyhrbEMK8rG3X/Fe34=
modernc.org/libc v1.14.3/go.mod h1:GPIvQVOVPizzlqyRX3l756/3ppsAgg1QgPxjr5Q4agQ= modernc.org/libc v1.14.3/go.mod h1:GPIvQVOVPizzlqyRX3l756/3ppsAgg1QgPxjr5Q4agQ=
modernc.org/libc v1.14.4 h1:Sw2GDhSQbQF75VKC6RSd5//QH2gj9TgBzjLNZTW6m00= modernc.org/libc v1.14.5 h1:DAHvwGoVRDZs5iJXnX9RJrgXSsorupCWmJ2ac964Owk=
modernc.org/libc v1.14.4/go.mod h1:GPIvQVOVPizzlqyRX3l756/3ppsAgg1QgPxjr5Q4agQ= modernc.org/libc v1.14.5/go.mod h1:2PJHINagVxO4QW/5OQdRrvMYo+bm5ClpUFfyXCYl9ak=
modernc.org/mathutil v1.1.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= modernc.org/mathutil v1.1.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
modernc.org/mathutil v1.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= modernc.org/mathutil v1.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
modernc.org/mathutil v1.4.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= modernc.org/mathutil v1.4.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=

View file

@ -42,13 +42,13 @@ func (group *Group) FromMap(m map[string]interface{}) {
type Groups struct { type Groups struct {
List []*Group List []*Group
mutex sync.RWMutex mutex sync.Mutex
} }
func NewGroups() *Groups { func NewGroups() *Groups {
return &Groups{ return &Groups{
List: []*Group{}, List: []*Group{},
mutex: sync.RWMutex{}, mutex: sync.Mutex{},
} }
} }
@ -69,8 +69,8 @@ func (groups *Groups) FromMap(f []interface{}) {
} }
func (groups *Groups) GetGroup(f interface{}) (group *Group, ok bool) { func (groups *Groups) GetGroup(f interface{}) (group *Group, ok bool) {
groups.mutex.RLock() groups.mutex.Lock()
defer groups.mutex.RUnlock() defer groups.mutex.Unlock()
switch v := f.(type) { switch v := f.(type) {
case uint: case uint:
@ -165,8 +165,8 @@ func (groups *Groups) Read(db *Database) error {
rows *sql.Rows rows *sql.Rows
) )
groups.mutex.RLock() groups.mutex.Lock()
defer groups.mutex.RUnlock() defer groups.mutex.Unlock()
groups.List = []*Group{} groups.List = []*Group{}

View file

@ -22,13 +22,13 @@ import (
type Livefeed struct { type Livefeed struct {
Matrix map[uint]map[uint]bool Matrix map[uint]map[uint]bool
mutex sync.RWMutex mutex sync.Mutex
} }
func NewLivefeed() *Livefeed { func NewLivefeed() *Livefeed {
return &Livefeed{ return &Livefeed{
Matrix: map[uint]map[uint]bool{}, Matrix: map[uint]map[uint]bool{},
mutex: sync.RWMutex{}, mutex: sync.Mutex{},
} }
} }
@ -66,8 +66,8 @@ func (livefeed *Livefeed) FromMap(f interface{}) {
} }
func (livefeed *Livefeed) IsAllOff() bool { func (livefeed *Livefeed) IsAllOff() bool {
livefeed.mutex.RLock() livefeed.mutex.Lock()
defer livefeed.mutex.RUnlock() defer livefeed.mutex.Unlock()
for _, sys := range livefeed.Matrix { for _, sys := range livefeed.Matrix {
for _, tg := range sys { for _, tg := range sys {
@ -81,8 +81,8 @@ func (livefeed *Livefeed) IsAllOff() bool {
} }
func (livefeed *Livefeed) IsEnabled(call *Call) bool { func (livefeed *Livefeed) IsEnabled(call *Call) bool {
livefeed.mutex.RLock() livefeed.mutex.Lock()
defer livefeed.mutex.RUnlock() defer livefeed.mutex.Unlock()
if call != nil { if call != nil {
if livefeed.Matrix[call.System][call.Talkgroup] { if livefeed.Matrix[call.System][call.Talkgroup] {

View file

@ -38,12 +38,12 @@ type Log struct {
} }
type Logs struct { type Logs struct {
mutex sync.RWMutex mutex sync.Mutex
} }
func NewLogs() *Logs { func NewLogs() *Logs {
return &Logs{ return &Logs{
mutex: sync.RWMutex{}, mutex: sync.Mutex{},
} }
} }
@ -94,8 +94,8 @@ func (logs *Logs) Search(searchOptions *LogsSearchOptions, db *Database) (*LogsS
where string = "true" where string = "true"
) )
logs.mutex.RLock() logs.mutex.Lock()
defer logs.mutex.RUnlock() defer logs.mutex.Unlock()
formatError := func(err error) error { formatError := func(err error) error {
return fmt.Errorf("newLogResults: %v", err) return fmt.Errorf("newLogResults: %v", err)

View file

@ -37,13 +37,13 @@ type Options struct {
TagsToggle bool `json:"tagsToggle"` TagsToggle bool `json:"tagsToggle"`
adminPassword string adminPassword string
adminPasswordNeedChange bool adminPasswordNeedChange bool
mutex sync.RWMutex mutex sync.Mutex
secret string secret string
} }
func NewOptions() *Options { func NewOptions() *Options {
return &Options{ return &Options{
mutex: sync.RWMutex{}, mutex: sync.Mutex{},
} }
} }
@ -129,8 +129,8 @@ func (options *Options) Read(db *Database) error {
f interface{} f interface{}
) )
options.mutex.RLock() options.mutex.Lock()
defer options.mutex.RUnlock() defer options.mutex.Unlock()
defaultPassword, _ = bcrypt.GenerateFromPassword([]byte(defaults.adminPassword), bcrypt.DefaultCost) defaultPassword, _ = bcrypt.GenerateFromPassword([]byte(defaults.adminPassword), bcrypt.DefaultCost)

View file

@ -43,7 +43,7 @@ func ParseSdrTrunkMeta(call *Call, controller *Controller) error {
s = regexp.MustCompile(`^([0-9]+)$`).FindStringSubmatch(m.Artist()) s = regexp.MustCompile(`^([0-9]+)$`).FindStringSubmatch(m.Artist())
if len(s) == 2 { if len(s) == 2 {
if i, err = strconv.Atoi(s[1]); err == nil { if i, err = strconv.Atoi(s[1]); err != nil {
return err return err
} }
if i > 0 { if i > 0 {
@ -271,22 +271,22 @@ func ParseMultipartContent(call *Call, p *multipart.Part, b []byte) {
} }
case "talkgroupGroup": case "talkgroupGroup":
if s := string(b); len(s) > 1 { if s := string(b); len(s) > 0 && s != "-" {
call.talkgroupGroup = s call.talkgroupGroup = s
} }
case "talkgroupLabel": case "talkgroupLabel":
if s := string(b); len(s) > 1 { if s := string(b); len(s) > 0 && s != "-" {
call.talkgroupLabel = s call.talkgroupLabel = s
} }
case "talkgroupName": case "talkgroupName":
if s := string(b); len(s) > 1 { if s := string(b); len(s) > 0 && s != "-" {
call.talkgroupName = s call.talkgroupName = s
} }
case "talkgroupTag": case "talkgroupTag":
if s := string(b); len(s) > 1 { if s := string(b); len(s) > 0 && s != "-" {
call.talkgroupTag = s call.talkgroupTag = s
} }
} }
@ -425,9 +425,9 @@ func ParseTrunkRecorderMeta(call *Call, b []byte) error {
switch v := m["talkgroup_tag"].(type) { switch v := m["talkgroup_tag"].(type) {
case string: case string:
call.talkgroupTag = v if len(v) > 0 && v != "-" {
call.talkgroupLabel = v call.talkgroupLabel = v
call.talkgroupName = v }
} }
return nil return nil

View file

@ -95,13 +95,13 @@ type SystemMap map[string]interface{}
type Systems struct { type Systems struct {
List []*System List []*System
mutex sync.RWMutex mutex sync.Mutex
} }
func NewSystems() *Systems { func NewSystems() *Systems {
return &Systems{ return &Systems{
List: []*System{}, List: []*System{},
mutex: sync.RWMutex{}, mutex: sync.Mutex{},
} }
} }
@ -122,8 +122,8 @@ func (systems *Systems) FromMap(f []interface{}) {
} }
func (systems *Systems) GetNewSystemId() uint { func (systems *Systems) GetNewSystemId() uint {
systems.mutex.RLock() systems.mutex.Lock()
defer systems.mutex.RUnlock() defer systems.mutex.Unlock()
NextId: NextId:
for i := uint(1); i < 65535; i++ { for i := uint(1); i < 65535; i++ {
@ -138,8 +138,8 @@ NextId:
} }
func (systems *Systems) GetSystem(f interface{}) (system *System, ok bool) { func (systems *Systems) GetSystem(f interface{}) (system *System, ok bool) {
systems.mutex.RLock() systems.mutex.Lock()
defer systems.mutex.RUnlock() defer systems.mutex.Unlock()
switch v := f.(type) { switch v := f.(type) {
case uint: case uint:
@ -319,8 +319,8 @@ func (systems *Systems) Read(db *Database) error {
rows *sql.Rows rows *sql.Rows
) )
systems.mutex.RLock() systems.mutex.Lock()
defer systems.mutex.RUnlock() defer systems.mutex.Unlock()
systems.List = []*System{} systems.List = []*System{}

View file

@ -42,13 +42,13 @@ func (tag *Tag) FromMap(m map[string]interface{}) {
type Tags struct { type Tags struct {
List []*Tag List []*Tag
mutex sync.RWMutex mutex sync.Mutex
} }
func NewTags() *Tags { func NewTags() *Tags {
return &Tags{ return &Tags{
List: []*Tag{}, List: []*Tag{},
mutex: sync.RWMutex{}, mutex: sync.Mutex{},
} }
} }
@ -69,8 +69,8 @@ func (tags *Tags) FromMap(f []interface{}) {
} }
func (tags *Tags) GetTag(f interface{}) (tag *Tag, ok bool) { func (tags *Tags) GetTag(f interface{}) (tag *Tag, ok bool) {
tags.mutex.RLock() tags.mutex.Lock()
defer tags.mutex.RUnlock() defer tags.mutex.Unlock()
switch v := f.(type) { switch v := f.(type) {
case uint: case uint:
@ -165,8 +165,8 @@ func (tags *Tags) Read(db *Database) error {
rows *sql.Rows rows *sql.Rows
) )
tags.mutex.RLock() tags.mutex.Lock()
defer tags.mutex.RUnlock() defer tags.mutex.Unlock()
tags.List = []*Tag{} tags.List = []*Tag{}

View file

@ -93,13 +93,13 @@ type TalkgroupMap map[string]interface{}
type Talkgroups struct { type Talkgroups struct {
List []*Talkgroup List []*Talkgroup
mutex sync.RWMutex mutex sync.Mutex
} }
func NewTalkgroups() *Talkgroups { func NewTalkgroups() *Talkgroups {
return &Talkgroups{ return &Talkgroups{
List: []*Talkgroup{}, List: []*Talkgroup{},
mutex: sync.RWMutex{}, mutex: sync.Mutex{},
} }
} }
@ -120,8 +120,8 @@ func (talkgroups *Talkgroups) FromMap(f []interface{}) {
} }
func (talkgroups *Talkgroups) GetTalkgroup(f interface{}) (system *Talkgroup, ok bool) { func (talkgroups *Talkgroups) GetTalkgroup(f interface{}) (system *Talkgroup, ok bool) {
talkgroups.mutex.RLock() talkgroups.mutex.Lock()
defer talkgroups.mutex.RUnlock() defer talkgroups.mutex.Unlock()
switch v := f.(type) { switch v := f.(type) {
case uint: case uint:
@ -149,8 +149,8 @@ func (talkgroups *Talkgroups) Read(db *Database, systemId uint) error {
rows *sql.Rows rows *sql.Rows
) )
talkgroups.mutex.RLock() talkgroups.mutex.Lock()
defer talkgroups.mutex.RUnlock() defer talkgroups.mutex.Unlock()
talkgroups.List = []*Talkgroup{} talkgroups.List = []*Talkgroup{}

View file

@ -44,13 +44,13 @@ func (unit *Unit) FromMap(m map[string]interface{}) {
type Units struct { type Units struct {
List []*Unit List []*Unit
mutex sync.RWMutex mutex sync.Mutex
} }
func NewUnits() *Units { func NewUnits() *Units {
return &Units{ return &Units{
List: []*Unit{}, List: []*Unit{},
mutex: sync.RWMutex{}, mutex: sync.Mutex{},
} }
} }
@ -88,6 +88,9 @@ func (units *Units) FromMap(f []interface{}) {
} }
func (u *Units) Merge(units *Units) { func (u *Units) Merge(units *Units) {
units.mutex.Lock()
defer units.mutex.Unlock()
for _, v := range units.List { for _, v := range units.List {
u.Add(v.Id, v.Label) u.Add(v.Id, v.Label)
} }
@ -99,8 +102,8 @@ func (units *Units) Read(db *Database, systemId uint) error {
rows *sql.Rows rows *sql.Rows
) )
units.mutex.RLock() units.mutex.Lock()
defer units.mutex.RUnlock() defer units.mutex.Unlock()
units.List = []*Unit{} units.List = []*Unit{}

View file

@ -15,4 +15,4 @@
package main package main
const Version = "6.1.6" const Version = "6.1.7"