mirror of
https://github.com/openmhz/trunk-server.git
synced 2026-11-12 08:47:40 -05:00
Merge branch 'master' into donate
This commit is contained in:
34
README.md
34
README.md
@@ -14,7 +14,7 @@ There are a bunch of experiments lurking in the code. There is some code for add
|
||||
|
||||
I haven't done a great job of keeping all the packages up to date... and I never got around to adding tests. Both of these would be great things for folks to go after.
|
||||
|
||||
## PROD vs DEV Env
|
||||
## PROD vs TEST Env
|
||||
The Prod environment expects you to be using HTTPS for your domains. It is pretty easy to use Let's Encrypt to grab Certs for the domains you are using. If not, just run things using the `docker-test.sh` script.
|
||||
|
||||
## Path Forward
|
||||
@@ -43,8 +43,9 @@ CNAMEs also need to be created for the various services. Create the CNAMEs below
|
||||
- api
|
||||
- account
|
||||
- admin
|
||||
- www
|
||||
|
||||
After doing this, you should have the following domains: `api.domain.com`, `account.domain.com`, `admin.domain.com`
|
||||
After doing this, you should have the following domains: `api.domain.com`, `account.domain.com`, `admin.domain.com`, `www.domain.com`
|
||||
|
||||
### S3 Storage
|
||||
Currently, both **test** and **prod** expect to use S3-based storage instead of local storage. Switching to use local storage would be relatively easy - but for the sake of testing, let's just say use something S3-compatible. Make sure that ~/.aws/credentials has the credentials you'd like to use with your S3-compatible storage provider.. IE:
|
||||
@@ -54,6 +55,29 @@ aws_access_key_id = [..]
|
||||
aws_secret_access_key = [..]
|
||||
```
|
||||
|
||||
# Automatically renewing SSL Certificates
|
||||
|
||||
SSL certs are automatically fetched from Let's Encrypt using the CertBot tool. The approach taken is based on this [Medium post](https://pentacent.medium.com/nginx-and-lets-encrypt-with-docker-in-less-than-5-minutes-b4b8a60d3a71) and accompanying [GitHub repo](https://github.com/wmnnd/nginx-certbot/tree/master).
|
||||
|
||||
You do need to jump start the process and do an initial fetch. To get started, make sure you have your `prod.env` file filled out. If you don't, copy prod.env.example to prod.env and fill in the details. Make sure **DOMAIN_NAME** and **REACT_APP_ADMIN_EMAIL** are correct and are the version you want to use in production. Those values will be used when requesting an SSL cert from Let's Encrypt.
|
||||
|
||||
This script uses Let's Encrypt's [Certbot tool](https://eff-certbot.readthedocs.io/en/latest/).
|
||||
|
||||
In the main directory of the **Trunk Server** repo, run the following commands:
|
||||
|
||||
```bash
|
||||
source prod.env
|
||||
docker compose -f certbot-compose.yml up
|
||||
```
|
||||
|
||||
Check the output from CertBot - and when it is done, just hit `ctrl + c` to exit.
|
||||
|
||||
And then run the following to make sure everything has stopped:
|
||||
|
||||
```bash
|
||||
docker compose -f certbot-compose.yml down
|
||||
```
|
||||
|
||||
### Configure
|
||||
The configurations for both **test** and **prod** come from environment variable files that are read in before the containers are started.
|
||||
Copy the example files and fill in the required info:
|
||||
@@ -218,14 +242,14 @@ It is a huge pain to upgrade MongoDB in place. It turns out to be easier to dump
|
||||
|
||||
Rough Playbook (use common sense, I may not have this exact):
|
||||
- get into the shell of the mongo container
|
||||
- `mongodump --db scanner —out /data/db/backup`
|
||||
- `mongodump --uri="mongodb://127.0.0.1" --db scanner --out /data/db/backup`
|
||||
- exit contianer and go back to host machine
|
||||
- `cd data/db`
|
||||
- `rm *` erase everything... but not the sub-directories because that is where the backup is
|
||||
- upgrade to the latest version of mongo
|
||||
- build and launch the mongo container, which will create an empty DB
|
||||
- get into the shell of the mongo container
|
||||
- `mongorestore --db scanner --drop /data/db/backup/`
|
||||
- `mongorestore --db scanner --drop /data/db/backup/scanner`
|
||||
|
||||
### Privlege Port Error
|
||||
|
||||
@@ -237,7 +261,7 @@ https://docs.docker.com/engine/security/rootless/#exposing-privileged-ports
|
||||
|
||||
## Setting up Logging
|
||||
I have had good luck with Loggly. Their free tier provides enough capabilities for most small sites.
|
||||
The Doocker Logging Driver works well and is easy to install:
|
||||
The Docker Logging Driver works well and is easy to install:
|
||||
|
||||
https://documentation.solarwinds.com/en/Success_Center/loggly/Content/admin/docker-logging-driver.htm
|
||||
|
||||
|
||||
@@ -110,7 +110,7 @@ exports.deleteGroup = async function (req, res, next) {
|
||||
|
||||
|
||||
exports.reorderGroups = function (req, res, next) {
|
||||
console.log(req.body)
|
||||
|
||||
//router.post('/system/:shortName/group_order', isLoggedIn, function(req, res) {
|
||||
process.nextTick(async function () {
|
||||
const system = await System.findOne({ shortName: req.params.shortName.toLowerCase() });
|
||||
@@ -131,7 +131,7 @@ exports.reorderGroups = function (req, res, next) {
|
||||
});
|
||||
return;
|
||||
}
|
||||
console.log(req.body)
|
||||
|
||||
if (!req.body.groupOrder) {
|
||||
res.status(500);
|
||||
res.json({
|
||||
@@ -142,8 +142,6 @@ exports.reorderGroups = function (req, res, next) {
|
||||
}
|
||||
var groupOrder = JSON.parse(req.body.groupOrder);
|
||||
for (var i = 0; i < groupOrder.length; i++) {
|
||||
|
||||
console.log("in group: " + groupOrder[i] + " position: " + i);
|
||||
const group = await Group.findOneAndUpdate(
|
||||
{
|
||||
_id: new mongoose.Types.ObjectId(groupOrder[i]),
|
||||
@@ -177,10 +175,8 @@ exports.reorderGroups = function (req, res, next) {
|
||||
};
|
||||
|
||||
exports.upsertGroup = function (req, res, next) {
|
||||
console.log(req.body)
|
||||
//router.post('/system/:shortName/group/:groupId?', isLoggedIn, function(req, res) {
|
||||
process.nextTick(async function () {
|
||||
console.log(req.body)
|
||||
let system = await System.findOne({ shortName: req.params.shortName.toLowerCase() });
|
||||
|
||||
if (!system) {
|
||||
|
||||
@@ -58,7 +58,6 @@ exports.listUserSystems = async function (req,res,next) {
|
||||
}
|
||||
const users = await User.find({}, "_id email firstName lastName lastLogin");
|
||||
let list = [];
|
||||
console.log(users);
|
||||
for (let index = 0; index < users.length; index++) {
|
||||
let user = users[index].toObject();
|
||||
const userId = new mongoose.Types.ObjectId(user._id);
|
||||
|
||||
@@ -94,7 +94,6 @@ async function csv_import(shortName, filename, callback) {
|
||||
|
||||
for (var i = 0; i < data.length; i++) {
|
||||
var row = data[i];
|
||||
console.log(row)
|
||||
if (!( "Decimal" in row) || !("Description" in row) || !("Alpha Tag" in row)) {
|
||||
console.error("Error: csv import column header error");
|
||||
callback("Column Headers are required - Required Columns: 'Decimal' 'Decscription' 'Alpha Tag' Optional Column: 'Priority' ", null);
|
||||
|
||||
23
backend/agents/honeycomb-tracing.js
Normal file
23
backend/agents/honeycomb-tracing.js
Normal file
@@ -0,0 +1,23 @@
|
||||
// tracing.js
|
||||
'use strict';
|
||||
|
||||
const { HoneycombSDK } = require('@honeycombio/opentelemetry-node');
|
||||
const {
|
||||
getNodeAutoInstrumentations,
|
||||
} = require('@opentelemetry/auto-instrumentations-node');
|
||||
|
||||
// uses the HONEYCOMB_API_KEY and OTEL_SERVICE_NAME environment variables
|
||||
const sdk = new HoneycombSDK({
|
||||
sampleRate: 5,
|
||||
instrumentations: [
|
||||
getNodeAutoInstrumentations({
|
||||
// we recommend disabling fs autoinstrumentation since it can be noisy
|
||||
// and expensive during startup
|
||||
'@opentelemetry/instrumentation-fs': {
|
||||
enabled: false,
|
||||
},
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
sdk.start();
|
||||
@@ -2,7 +2,7 @@
|
||||
const { ObjectId } = require('mongodb');
|
||||
var { callModel: Call } = require("../models/call");
|
||||
const Group = require("../models/group");
|
||||
|
||||
const opentelemetry = require('@opentelemetry/api');
|
||||
var defaultNumResults = 50;
|
||||
|
||||
var channels = {};
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
var path = require('path');
|
||||
var fs = require('fs');
|
||||
const opentelemetry = require('@opentelemetry/api');
|
||||
var sysStats = require("../sys_stats");
|
||||
var System = require("../models/system");
|
||||
var Talkgroup = require("../models/talkgroup");
|
||||
var { callModel: Call } = require("../models/call");
|
||||
|
||||
|
||||
const { PutObjectCommand, S3Client } = require("@aws-sdk/client-s3");
|
||||
const { fromIni } = require("@aws-sdk/credential-providers");
|
||||
|
||||
@@ -18,183 +20,236 @@ const client = new S3Client({
|
||||
credentials: fromIni({ profile: s3_profile }),
|
||||
endpoint: s3_endpoint,
|
||||
region: s3_region,
|
||||
maxAttempts: 1
|
||||
maxAttempts: 3
|
||||
});
|
||||
|
||||
exports.upload = async function (req, res, next) {
|
||||
process.nextTick( async () => {
|
||||
if (!req.file || ((path.extname(req.file.originalname) != '.m4a') && (path.extname(req.file.originalname) != '.mp3'))) {
|
||||
console.warn("[" + req.params.shortName + "] Error file name is wrong or file does not exist");
|
||||
res.status(500);
|
||||
res.send("Error, invalid filename:\n");
|
||||
return;
|
||||
}
|
||||
var shortName = req.params.shortName.toLowerCase();
|
||||
var apiKey = req.body.api_key;
|
||||
let item = null;
|
||||
|
||||
try {
|
||||
item = await System.findOne({ 'shortName': shortName }, ["key", "ignoreUnknownTalkgroup"]);
|
||||
} catch (err) {
|
||||
console.warn("[" + req.params.shortName + "] Error /:shortName/upload - Error: " + err);
|
||||
res.status(500);
|
||||
res.send("Error, invalid shotName:\n" + err);
|
||||
return;
|
||||
}
|
||||
process.nextTick(async () => {
|
||||
if (!req.file || ((path.extname(req.file.originalname) != '.m4a') && (path.extname(req.file.originalname) != '.mp3'))) {
|
||||
console.warn("[" + req.params.shortName + "] Error file name is wrong or file does not exist");
|
||||
res.status(500);
|
||||
res.send("Error, invalid filename:\n");
|
||||
return;
|
||||
}
|
||||
var shortName = req.params.shortName.toLowerCase();
|
||||
var apiKey = req.body.api_key;
|
||||
var talkgroupNum = parseInt(req.body.talkgroup_num);
|
||||
var freq = parseFloat(req.body.freq);
|
||||
var time = new Date(parseInt(req.body.start_time) * 1000);
|
||||
var stopTime = new Date();
|
||||
if (req.body.stop_time) {
|
||||
var stopTime = new Date(parseInt(req.body.stop_time) * 1000);
|
||||
}
|
||||
var startTime = req.body.start_time;
|
||||
var emergency = parseInt(req.body.emergency);
|
||||
|
||||
if (!item) {
|
||||
console.info("[" + req.params.shortName + "] Error /:shortName/upload ShortName does not exist");
|
||||
res.status(500);
|
||||
res.send("ShortName does not exist: " + shortName + "\n");
|
||||
return;
|
||||
}
|
||||
if (apiKey != item.key) {
|
||||
console.warn("[" + req.params.shortName + "] Error /:shortName/upload API Key Mismatch - Provided key: " + apiKey);
|
||||
res.status(500);
|
||||
res.send("API Keys do not match!\n");
|
||||
return;
|
||||
}
|
||||
let errorCount = parseInt(req.body.error_count);
|
||||
let spikeCount = parseInt(req.body.spike_count);
|
||||
|
||||
var talkgroupNum = parseInt(req.body.talkgroup_num);
|
||||
var freq = parseFloat(req.body.freq);
|
||||
var time = new Date(parseInt(req.body.start_time) * 1000);
|
||||
var stopTime = new Date();
|
||||
if (req.body.stop_time) {
|
||||
var stopTime = new Date(parseInt(req.body.stop_time) * 1000);
|
||||
}
|
||||
var startTime = req.body.start_time;
|
||||
var emergency = parseInt(req.body.emergency);
|
||||
if (Number.isNaN(errorCount)) {
|
||||
errorCount = 0;
|
||||
}
|
||||
if (Number.isNaN(spikeCount)) {
|
||||
spikeCount = 0;
|
||||
}
|
||||
|
||||
// Blocking sensitive talkgroups
|
||||
if ((shortName == "hennearmer") && ((talkgroupNum == 3421) || (talkgroupNum == 3423))) {
|
||||
res.status(200).end();
|
||||
return;
|
||||
}
|
||||
try {
|
||||
var srcList = JSON.parse(req.body.source_list);
|
||||
} catch (err) {
|
||||
var srcList = [];
|
||||
console.warn("[" + req.params.shortName + "] Error /:shortName/upload Parsing Source/Freq List - Error: " + err);
|
||||
res.status(500);
|
||||
res.send("Error parsing sourcelist " + err);
|
||||
return;
|
||||
}
|
||||
|
||||
let item = null;
|
||||
const span = opentelemetry.trace.getActiveSpan();
|
||||
span.setAttribute('call.shortName', shortName);
|
||||
span.setAttribute('call.talkgroupNum', talkgroupNum);
|
||||
span.setAttribute('call.startTime', startTime);
|
||||
|
||||
|
||||
if (item.ignoreUnknownTalkgroup == true) {
|
||||
talkgroupExists = await Talkgroup.exists({
|
||||
'shortName': shortName,
|
||||
'num': talkgroupNum
|
||||
|
||||
// Validate that the system exists and the API key is correct
|
||||
|
||||
try {
|
||||
item = await System.findOne({ 'shortName': shortName }, ["key", "ignoreUnknownTalkgroup"]);
|
||||
} catch (err) {
|
||||
span.setStatus({
|
||||
code: opentelemetry.SpanStatusCode.ERROR,
|
||||
message: "Error /:shortName/upload - Error: " + err,
|
||||
});
|
||||
console.warn("[" + req.params.shortName + "] Error /:shortName/upload - Error: " + err);
|
||||
res.status(500);
|
||||
res.send("Error, invalid shortName:\n" + err);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!item) {
|
||||
span.setStatus({
|
||||
code: opentelemetry.SpanStatusCode.ERROR,
|
||||
message: "Error /:shortName/upload API Key Mismatch - Provided key: " + apiKey,
|
||||
});
|
||||
console.info("[" + req.params.shortName + "] Error /:shortName/upload ShortName does not exist");
|
||||
res.status(500);
|
||||
res.send("ShortName does not exist: " + shortName + "\n");
|
||||
return;
|
||||
}
|
||||
if (apiKey != item.key) {
|
||||
span.setStatus({
|
||||
code: opentelemetry.SpanStatusCode.ERROR,
|
||||
message: "Error /:shortName/upload API Key Mismatch - Provided key: " + apiKey,
|
||||
});
|
||||
console.warn("[" + req.params.shortName + "] Error /:shortName/upload API Key Mismatch - Provided key: " + apiKey);
|
||||
res.status(500);
|
||||
res.send("API Keys do not match!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// Blocking sensitive talkgroups
|
||||
if ((shortName == "hennearmer") && ((talkgroupNum == 3421) || (talkgroupNum == 3423))) {
|
||||
span.setStatus({
|
||||
code: opentelemetry.SpanStatusCode.ERROR,
|
||||
message: "Sensitive Talkgroup",
|
||||
});
|
||||
res.status(200).end();
|
||||
return;
|
||||
}
|
||||
|
||||
if (item.ignoreUnknownTalkgroup == true) {
|
||||
talkgroupExists = await Talkgroup.exists({
|
||||
'shortName': shortName,
|
||||
'num': talkgroupNum
|
||||
});
|
||||
|
||||
if (!talkgroupExists) {
|
||||
try {
|
||||
fs.unlinkSync(req.file.path)
|
||||
//file removed
|
||||
} catch (err) {
|
||||
console.log("[" + call.shortName + "] error deleting: " + req.file.path);
|
||||
}
|
||||
|
||||
res.status(500);
|
||||
res.send("Talkgroup does not exist, skipping.\n");
|
||||
span.setStatus({
|
||||
code: opentelemetry.SpanStatusCode.ERROR,
|
||||
message: "Ignore Unknown Talkgroup Set - Talkgroup does not exist: " + talkgroupNum + " ShortName: " + shortName,
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Add in an API Key check
|
||||
|
||||
|
||||
|
||||
res.status(200).end();
|
||||
|
||||
|
||||
var local_path = "/" + shortName + "/" + time.getFullYear() + "/" + (time.getMonth() + 1) + "/" + time.getDate() + "/";
|
||||
var object_key = "media/" + shortName + "-" + talkgroupNum + "-" + startTime + path.extname(req.file.originalname);
|
||||
|
||||
var endpoint = s3_endpoint;
|
||||
var bucket = s3_bucket;
|
||||
var url = s3_endpoint + "/" + s3_bucket + "/" + object_key;
|
||||
|
||||
var call = new Call({
|
||||
shortName: shortName,
|
||||
talkgroupNum: talkgroupNum,
|
||||
objectKey: object_key,
|
||||
endpoint: endpoint,
|
||||
bucket: bucket,
|
||||
time: time,
|
||||
name: talkgroupNum + "-" + startTime + ".m4a",
|
||||
freq: freq,
|
||||
errorCount: errorCount,
|
||||
spikeCount: spikeCount,
|
||||
url: url,
|
||||
emergency: emergency,
|
||||
path: local_path,
|
||||
srcList: srcList,
|
||||
len: -1
|
||||
});
|
||||
|
||||
if (!talkgroupExists) {
|
||||
try {
|
||||
fs.unlinkSync(req.file.path)
|
||||
//file removed
|
||||
} catch (err) {
|
||||
console.log("[" + call.shortName + "] error deleting: " + req.file.path);
|
||||
|
||||
try {
|
||||
if (req.body.call_length) {
|
||||
call.len = parseFloat(req.body.call_length);
|
||||
} else {
|
||||
call.len = (stopTime - time) / 1000;
|
||||
}
|
||||
|
||||
res.status(500);
|
||||
res.send("Talkgroup does not exist, skipping.\n");
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
let fileContent;
|
||||
try {
|
||||
fileContent = fs.readFileSync(req.file.path);
|
||||
//s3Src = fs.createReadStream(req.file.path);
|
||||
} catch (err) {
|
||||
span.setStatus({
|
||||
code: opentelemetry.SpanStatusCode.ERROR,
|
||||
message: "Unable to open file: " + req.file.path + " Error: " + err,
|
||||
});
|
||||
console.error("[" + call.shortName + "] Unable to open file: " + req.file.path + " Error: " + err);
|
||||
return;
|
||||
|
||||
}
|
||||
}
|
||||
// Add in an API Key check
|
||||
const command = new PutObjectCommand({
|
||||
Bucket: s3_bucket,
|
||||
Key: object_key,
|
||||
Body: fileContent,
|
||||
ACL: 'public-read'
|
||||
});
|
||||
|
||||
let errorCount = parseInt(req.body.error_count);
|
||||
let spikeCount = parseInt(req.body.spike_count);
|
||||
try {
|
||||
await client.send(command);
|
||||
|
||||
if (Number.isNaN(errorCount)) {
|
||||
errorCount = 0;
|
||||
}
|
||||
if (Number.isNaN(spikeCount)) {
|
||||
spikeCount = 0;
|
||||
}
|
||||
|
||||
try {
|
||||
var srcList = JSON.parse(req.body.source_list);
|
||||
} catch (err) {
|
||||
var srcList = [];
|
||||
console.warn("[" + req.params.shortName + "] Error /:shortName/upload Parsing Source/Freq List - Error: " + err);
|
||||
res.status(500);
|
||||
res.send("Error parsing sourcelist " + err);
|
||||
return;
|
||||
}
|
||||
|
||||
res.status(200).end();
|
||||
|
||||
var local_path = "/" + shortName + "/" + time.getFullYear() + "/" + (time.getMonth() + 1) + "/" + time.getDate() + "/";
|
||||
var object_key = "media/" + shortName + "-" + talkgroupNum + "-" + startTime + path.extname(req.file.originalname);
|
||||
|
||||
var endpoint = s3_endpoint;
|
||||
var bucket = s3_bucket;
|
||||
var url = s3_endpoint + "/" + s3_bucket + "/" + object_key;
|
||||
|
||||
var call = new Call({
|
||||
shortName: shortName,
|
||||
talkgroupNum: talkgroupNum,
|
||||
objectKey: object_key,
|
||||
endpoint: endpoint,
|
||||
bucket: bucket,
|
||||
time: time,
|
||||
name: talkgroupNum + "-" + startTime + ".m4a",
|
||||
freq: freq,
|
||||
errorCount: errorCount,
|
||||
spikeCount: spikeCount,
|
||||
url: url,
|
||||
emergency: emergency,
|
||||
path: local_path,
|
||||
srcList: srcList,
|
||||
len: -1
|
||||
});
|
||||
|
||||
try {
|
||||
if (req.body.call_length) {
|
||||
call.len = parseFloat(req.body.call_length);
|
||||
} else {
|
||||
call.len = (stopTime - time) / 1000;
|
||||
} catch (err) {
|
||||
span.setStatus({
|
||||
code: opentelemetry.SpanStatusCode.ERROR,
|
||||
message: "Upload Error: " + err + " Filename: " + call.name + " content-length: " + req.headers['content-length'] + " Key: " + object_key + " Bucket: " + s3_bucket,
|
||||
});
|
||||
console.warn("[" + call.shortName + "] Upload Error: " + err + " Filename: " + call.name + " content-length: " + req.headers['content-length'] + " Key: " + object_key + " Bucket: " + s3_bucket);
|
||||
/*res.status(500);
|
||||
sysStats.addError(call.toObject());
|
||||
res.contentType('json');
|
||||
res.status(500);
|
||||
res.send(JSON.stringify({
|
||||
success: false,
|
||||
error: "File Upload"
|
||||
}));*/
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
let fileContent;
|
||||
try {
|
||||
fileContent = fs.readFileSync(req.file.path);
|
||||
//s3Src = fs.createReadStream(req.file.path);
|
||||
} catch (err) {
|
||||
console.error("[" + call.shortName + "] Unable to open file: " + req.file.path + " Error: " + err);
|
||||
return;
|
||||
}
|
||||
const command = new PutObjectCommand({
|
||||
Bucket: s3_bucket,
|
||||
Key: object_key,
|
||||
Body: fileContent,
|
||||
ACL: 'public-read'
|
||||
});
|
||||
|
||||
try {
|
||||
await client.send(command);
|
||||
await call.save();
|
||||
sysStats.addCall(call.toObject());
|
||||
// we only want to notify clients if the clip is longer than 1 second.
|
||||
if (call.len >= 1) {
|
||||
req.call = call.toObject();
|
||||
next();
|
||||
|
||||
try {
|
||||
await call.save();
|
||||
sysStats.addCall(call.toObject());
|
||||
// we only want to notify clients if the clip is longer than 1 second.
|
||||
if (call.len >= 1) {
|
||||
req.call = call.toObject();
|
||||
next();
|
||||
}
|
||||
} catch (err) {
|
||||
console.warn("[" + call.shortName + "] Error saving call: " + err);
|
||||
span.setStatus({
|
||||
code: opentelemetry.SpanStatusCode.ERROR,
|
||||
message: "Error saving call: " + err,
|
||||
});
|
||||
/*res.status(500);
|
||||
sysStats.addError(call.toObject());
|
||||
res.contentType('json');
|
||||
res.status(500);
|
||||
res.send(JSON.stringify({
|
||||
success: false,
|
||||
error: "File Upload"
|
||||
}));*/
|
||||
}
|
||||
} catch (err) {
|
||||
console.warn("[" + call.shortName + "] Upload Error: " + err + " Filename: " + call.name + " content-length: " + req.headers['content-length'] + " Key: " + object_key + " Bucket: " + s3_bucket);
|
||||
/*res.status(500);
|
||||
sysStats.addError(call.toObject());
|
||||
res.contentType('json');
|
||||
res.status(500);
|
||||
res.send(JSON.stringify({
|
||||
success: false,
|
||||
error: "File Upload"
|
||||
}));*/
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
fs.unlinkSync(req.file.path);
|
||||
} catch (err) {
|
||||
console.warn("There was an Error uploading an deleting: " + req.file.path);
|
||||
};
|
||||
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
|
||||
const tracing = require("./agents/honeycomb-tracing");
|
||||
var express = require("express");
|
||||
var configureExpress = require("./config/express");
|
||||
|
||||
|
||||
2631
backend/package-lock.json
generated
2631
backend/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -6,6 +6,9 @@
|
||||
"@aws-sdk/client-s3": "^3.309.0",
|
||||
"@aws-sdk/credential-provider-process": "^3.306.0",
|
||||
"@aws-sdk/credential-providers": "^3.309.0",
|
||||
"@honeycombio/opentelemetry-node": "^0.6.1",
|
||||
"@opentelemetry/api": "^1.7.0",
|
||||
"@opentelemetry/auto-instrumentations-node": "^0.41.0",
|
||||
"adm-zip": "^0.5.10",
|
||||
"aws-sdk": "^2.1353",
|
||||
"axios": "1.3.5",
|
||||
@@ -22,12 +25,12 @@
|
||||
"ffprobe-static": "3.1.0",
|
||||
"find-remove": "4.1.0",
|
||||
"get-folder-size": "^4.0.0",
|
||||
"node-mailjet": "6.0.2",
|
||||
"mkdirp": "^2.1.6",
|
||||
"mongodb": "5.2.0",
|
||||
"mongoose": "7.0.3",
|
||||
"multer": "1.3.0",
|
||||
"node-ffprobe": "https://github.com/ListenerApproved/node-ffprobe",
|
||||
"node-mailjet": "6.0.2",
|
||||
"node-schedule": "2.1.1",
|
||||
"passport": "0.6.0",
|
||||
"passport-local": "1.0.0",
|
||||
|
||||
@@ -10,10 +10,7 @@ var spots = (24 * 60) / timePeriod; // the number of spots needed to keep track
|
||||
|
||||
|
||||
async function updateActiveSystems() {
|
||||
|
||||
// Go through all of the Systems
|
||||
|
||||
|
||||
for await (let item of System.find()) {
|
||||
// go through all the systems
|
||||
// if you have recieved some calls during that last period, make the system active
|
||||
|
||||
37
certbot-compose.yml
Normal file
37
certbot-compose.yml
Normal file
@@ -0,0 +1,37 @@
|
||||
version: "3"
|
||||
services:
|
||||
nginx:
|
||||
build:
|
||||
context: ./nginx-proxy
|
||||
args:
|
||||
STAGE: "cert"
|
||||
environment:
|
||||
- DOMAIN_NAME
|
||||
image: "nginx:${TAG}-cert"
|
||||
networks:
|
||||
- proxy
|
||||
ports:
|
||||
- "80:80"
|
||||
restart: always
|
||||
volumes:
|
||||
- ${PWD}/data/log/syslog:/var/log/syslog
|
||||
- ${PWD}/data/log/nginx:/var/log/nginx
|
||||
- ${PWD}/data/certbot/www/:/var/www/certbot/:ro
|
||||
- ${PWD}/data/certbot/conf/:/etc/letsencrypt/:ro
|
||||
command: /bin/bash -c "envsubst '$$DOMAIN_NAME' < /etc/nginx/conf.d/site.template > /etc/nginx/conf.d/default.conf && while :; do sleep 6h & wait $${!}; nginx -s reload; done & nginx -g 'daemon off;'"
|
||||
|
||||
certbot:
|
||||
image: certbot/certbot:latest
|
||||
volumes:
|
||||
- ${PWD}/data/certbot/www/:/var/www/certbot/:rw
|
||||
- ${PWD}/data/certbot/conf/:/etc/letsencrypt/:rw
|
||||
entrypoint: "certbot certonly --webroot -w /var/www/certbot \
|
||||
--email ${REACT_APP_ADMIN_EMAIL} \
|
||||
-d ${DOMAIN_NAME} -d www.${DOMAIN_NAME} -d api.${DOMAIN_NAME} -d admin.${DOMAIN_NAME} -d account.${DOMAIN_NAME} \
|
||||
--rsa-key-size 4096 \
|
||||
--agree-tos \
|
||||
--force-renewal"
|
||||
|
||||
networks:
|
||||
proxy:
|
||||
node:
|
||||
@@ -33,6 +33,10 @@ services:
|
||||
- ${PWD}/data/certbot/www/:/var/www/certbot/:rw
|
||||
- ${PWD}/data/certbot/conf/:/etc/letsencrypt/:rw
|
||||
entrypoint: "/bin/sh -c 'trap exit TERM; while :; do certbot renew; sleep 12h & wait $${!}; done;'"
|
||||
logging:
|
||||
driver: syslog
|
||||
options:
|
||||
tag: "certbot"
|
||||
|
||||
mongo:
|
||||
build:
|
||||
@@ -73,6 +77,8 @@ services:
|
||||
- S3_BUCKET
|
||||
- S3_PROFILE
|
||||
- S3_REGION
|
||||
- OTEL_SERVICE_NAME
|
||||
- HONEYCOMB_API_KEY
|
||||
volumes:
|
||||
- "~/.aws:/home/app/.aws"
|
||||
depends_on:
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
source test.env
|
||||
echo "Domains: " $DOMAIN_NAME
|
||||
echo "Protocol: " $PROTOCOL
|
||||
echo "Docker Compose Command: " $@
|
||||
docker compose -f docker-compose.yml -f test-compose.yml $@
|
||||
|
||||
@@ -66,15 +66,17 @@ const MediaPlayer = (props) => {
|
||||
const audio = audioRef.current.audioEl.current;
|
||||
|
||||
setSourceIndex(0);
|
||||
navigator.mediaSession.metadata = new MediaMetadata({
|
||||
title: "Waiting for Call...",
|
||||
album: 'OpenMHz',
|
||||
artwork: [
|
||||
{ src: '/favicon-32x32.png', sizes: '32x32', type: 'image/png' },
|
||||
{ src: '/android-chrome-192x192.png', sizes: '192x192', type: 'image/png' },
|
||||
{ src: '/android-chrome-192x192.png', sizes: '512x512', type: 'image/png' },
|
||||
]
|
||||
});
|
||||
if ('mediaSession' in navigator) {
|
||||
navigator.mediaSession.metadata = new MediaMetadata({
|
||||
title: "Waiting for Call...",
|
||||
album: 'OpenMHz',
|
||||
artwork: [
|
||||
{ src: '/favicon-32x32.png', sizes: '32x32', type: 'image/png' },
|
||||
{ src: '/android-chrome-192x192.png', sizes: '192x192', type: 'image/png' },
|
||||
{ src: '/android-chrome-192x192.png', sizes: '512x512', type: 'image/png' },
|
||||
]
|
||||
});
|
||||
}
|
||||
audio.src = "data:audio/mpeg;base64,SUQzBAAAAAAAI1RTU0UAAAAPAAADTGF2ZjU4Ljc2LjEwMAAAAAAAAAAAAAAA/+M4wAAAAAAAAAAAAEluZm8AAAAPAAAAAwAAAbAAqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq1dXV1dXV1dXV1dXV1dXV1dXV1dXV1dXV1dXV1dXV1dXV////////////////////////////////////////////AAAAAExhdmM1OC4xMwAAAAAAAAAAAAAAACQDkAAAAAAAAAGw9wrNaQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/+MYxAAAAANIAAAAAExBTUUzLjEwMFVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV/+MYxDsAAANIAAAAAFVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV/+MYxHYAAANIAAAAAFVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV";
|
||||
const playPromise = audio.play();
|
||||
|
||||
|
||||
@@ -1,91 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# from https://github.com/wmnnd/nginx-certbot/blob/master/init-letsencrypt.sh
|
||||
|
||||
|
||||
# Load environment variables from prod.env.example
|
||||
if [ -f prod.env ]; then
|
||||
source test.env
|
||||
fi
|
||||
|
||||
# Rest of the script...
|
||||
|
||||
if ! [ -x "$(command -v docker compose)" ]; then
|
||||
echo 'Error: docker compose is not installed.' >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
domains=("$DOMAIN_NAME" "account.$DOMAIN_NAME" "admin.$DOMAIN_NAME" "api.$DOMAIN_NAME" "www.$DOMAIN_NAME")
|
||||
|
||||
rsa_key_size=4096
|
||||
data_path="./data/certbot"
|
||||
email=$REACT_APP_ADMIN_EMAIL
|
||||
staging=0 # Set to 1 if you're testing your setup to avoid hitting request limits
|
||||
|
||||
if [ -d "$data_path" ]; then
|
||||
read -p "Existing data found for $domains. Continue and replace existing certificate? (y/N) " decision
|
||||
if [ "$decision" != "Y" ] && [ "$decision" != "y" ]; then
|
||||
exit
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
if [ ! -e "$data_path/conf/options-ssl-nginx.conf" ] || [ ! -e "$data_path/conf/ssl-dhparams.pem" ]; then
|
||||
echo "### Downloading recommended TLS parameters ..."
|
||||
mkdir -p "$data_path/conf"
|
||||
curl -s https://raw.githubusercontent.com/certbot/certbot/master/certbot-nginx/certbot_nginx/_internal/tls_configs/options-ssl-nginx.conf > "$data_path/conf/options-ssl-nginx.conf"
|
||||
curl -s https://raw.githubusercontent.com/certbot/certbot/master/certbot/certbot/ssl-dhparams.pem > "$data_path/conf/ssl-dhparams.pem"
|
||||
echo
|
||||
fi
|
||||
|
||||
echo "### Creating dummy certificate for $domains ..."
|
||||
path="/etc/letsencrypt/live/$domains"
|
||||
mkdir -p "$data_path/conf/live/$domains"
|
||||
docker compose -f docker-compose.yml -f test-compose.yml run --rm --entrypoint "\
|
||||
openssl req -x509 -nodes -newkey rsa:$rsa_key_size -days 1\
|
||||
-keyout '$path/privkey.pem' \
|
||||
-out '$path/fullchain.pem' \
|
||||
-subj '/CN=localhost'" certbot
|
||||
echo
|
||||
|
||||
|
||||
echo "### Starting nginx ..."
|
||||
docker compose -f docker-compose.yml -f test-compose.yml up -d nginx
|
||||
echo
|
||||
|
||||
echo "### Deleting dummy certificate for $domains ..."
|
||||
docker compose -f docker-compose.yml -f test-compose.yml run --rm --entrypoint "\
|
||||
rm -Rf /etc/letsencrypt/live/$domains && \
|
||||
rm -Rf /etc/letsencrypt/archive/$domains && \
|
||||
rm -Rf /etc/letsencrypt/renewal/$domains.conf" certbot
|
||||
echo
|
||||
|
||||
|
||||
echo "### Requesting Let's Encrypt certificate for $domains ..."
|
||||
#Join $domains to -d args
|
||||
domain_args=""
|
||||
for domain in "${domains[@]}"; do
|
||||
domain_args="$domain_args -d $domain"
|
||||
done
|
||||
|
||||
# Select appropriate email arg
|
||||
case "$email" in
|
||||
"") email_arg="--register-unsafely-without-email" ;;
|
||||
*) email_arg="--email $email" ;;
|
||||
esac
|
||||
|
||||
# Enable staging mode if needed
|
||||
if [ $staging != "0" ]; then staging_arg="--staging"; fi
|
||||
|
||||
docker compose -f docker-compose.yml -f test-compose.yml run --rm --entrypoint "\
|
||||
certbot certonly --webroot -w /var/www/certbot \
|
||||
$staging_arg \
|
||||
$email_arg \
|
||||
$domain_args \
|
||||
--rsa-key-size $rsa_key_size \
|
||||
--agree-tos \
|
||||
--force-renewal" certbot
|
||||
echo
|
||||
|
||||
echo "### Reloading nginx ..."
|
||||
docker compose -f docker-compose.yml -f test-compose.yml exec nginx nginx -s reload
|
||||
@@ -2,7 +2,7 @@
|
||||
#
|
||||
# docker build -t mongo .
|
||||
|
||||
FROM mongo:5.0
|
||||
FROM mongo:7-jammy
|
||||
|
||||
MAINTAINER Luke Berndt <lukekb@gmail.com>
|
||||
|
||||
|
||||
@@ -21,7 +21,6 @@ RUN apt-get update \
|
||||
|
||||
# Add main NGINX config
|
||||
COPY *.conf /etc/nginx/
|
||||
COPY ${STAGE}/certs/ /etc/nginx/certs/
|
||||
|
||||
# Add virtual hosts
|
||||
COPY ${STAGE}/vhosts/ /etc/nginx/conf.d/
|
||||
|
||||
8
nginx-proxy/cert/vhosts/site.template
Normal file
8
nginx-proxy/cert/vhosts/site.template
Normal file
@@ -0,0 +1,8 @@
|
||||
server {
|
||||
listen 80;
|
||||
server_name account.${DOMAIN_NAME} api.${DOMAIN_NAME} admin.${DOMAIN_NAME} www.${DOMAIN_NAME} ${DOMAIN_NAME};
|
||||
|
||||
location /.well-known/acme-challenge/ {
|
||||
root /var/www/certbot;
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
# Copy your certs here
|
||||
@@ -27,6 +27,10 @@ server {
|
||||
|
||||
server_name ${DOMAIN_NAME} www.${DOMAIN_NAME};
|
||||
|
||||
location /.well-known/acme-challenge/ {
|
||||
root /var/www/certbot;
|
||||
}
|
||||
|
||||
location /static {
|
||||
proxy_pass http://frontend:3000;
|
||||
access_log off;
|
||||
@@ -63,6 +67,10 @@ server {
|
||||
|
||||
server_name api.${DOMAIN_NAME};
|
||||
|
||||
location /.well-known/acme-challenge/ {
|
||||
root /var/www/certbot;
|
||||
}
|
||||
|
||||
location /marcsip/upload {
|
||||
deny all;
|
||||
}
|
||||
@@ -100,6 +108,10 @@ server {
|
||||
|
||||
server_name account.${DOMAIN_NAME};
|
||||
|
||||
location /.well-known/acme-challenge/ {
|
||||
root /var/www/certbot;
|
||||
}
|
||||
|
||||
location / {
|
||||
proxy_pass http://account:3009;
|
||||
proxy_http_version 1.1;
|
||||
@@ -127,6 +139,10 @@ server {
|
||||
|
||||
server_name admin.${DOMAIN_NAME};
|
||||
|
||||
location /.well-known/acme-challenge/ {
|
||||
root /var/www/certbot;
|
||||
}
|
||||
|
||||
location / {
|
||||
proxy_pass http://admin:3008;
|
||||
proxy_http_version 1.1;
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
# Copy your certs here
|
||||
@@ -1,8 +1,14 @@
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
|
||||
server_name ${DOMAIN_NAME} www.${DOMAIN_NAME};
|
||||
|
||||
|
||||
location /.well-known/acme-challenge/ {
|
||||
root /var/www/certbot;
|
||||
}
|
||||
|
||||
location /static {
|
||||
proxy_pass http://frontend:3000;
|
||||
access_log off;
|
||||
@@ -30,6 +36,10 @@ server {
|
||||
|
||||
server_name account.${DOMAIN_NAME};
|
||||
|
||||
location /.well-known/acme-challenge/ {
|
||||
root /var/www/certbot;
|
||||
}
|
||||
|
||||
location / {
|
||||
proxy_pass http://account:3009;
|
||||
include /etc/nginx/proxy.conf;
|
||||
@@ -42,6 +52,10 @@ server {
|
||||
|
||||
server_name admin.${DOMAIN_NAME};
|
||||
|
||||
location /.well-known/acme-challenge/ {
|
||||
root /var/www/certbot;
|
||||
}
|
||||
|
||||
location / {
|
||||
proxy_pass http://admin:3008;
|
||||
include /etc/nginx/proxy.conf;
|
||||
@@ -54,6 +68,10 @@ server {
|
||||
|
||||
server_name api.${DOMAIN_NAME};
|
||||
|
||||
location /.well-known/acme-challenge/ {
|
||||
root /var/www/certbot;
|
||||
}
|
||||
|
||||
location / {
|
||||
proxy_pass http://backend:3005;
|
||||
include /etc/nginx/proxy.conf;
|
||||
|
||||
Reference in New Issue
Block a user