mirror of
https://github.com/openmhz/trunk-server.git
synced 2026-11-12 16:57:39 -05:00
Twitter card support
This commit is contained in:
30
frontend/server/card.ejs
Normal file
30
frontend/server/card.ejs
Normal file
@@ -0,0 +1,30 @@
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
<title>wavesurfer.js | Media Element Example</title>
|
||||
<script src="https://unpkg.com/wavesurfer.js"></script>
|
||||
<script src="/card.js"></script>
|
||||
</head>
|
||||
|
||||
<body >
|
||||
|
||||
<div id="demo">
|
||||
<div id="waveform">
|
||||
<!-- Here be the waveform -->
|
||||
</div>
|
||||
|
||||
<div class="controls">
|
||||
<button class="btn btn-primary" data-action="play">
|
||||
<i class="glyphicon glyphicon-play"></i>
|
||||
Play
|
||||
/
|
||||
<i class="glyphicon glyphicon-pause"></i>
|
||||
Pause
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
67
frontend/server/config/express.js
Normal file
67
frontend/server/config/express.js
Normal file
@@ -0,0 +1,67 @@
|
||||
const path = require("path");
|
||||
const express = require("express");
|
||||
const secrets = require("./secrets");
|
||||
|
||||
|
||||
var cookie_domain = process.env['REACT_APP_COOKIE_DOMAIN'] != null ? process.env['REACT_APP_COOKIE_DOMAIN'] : '.openmhz.com'; //'https://s3.amazonaws.com/robotastic';
|
||||
var backend_server = process.env['REACT_APP_BACKEND_SERVER'] != null ? process.env['REACT_APP_BACKEND_SERVER'] : 'https://api.openmhz.com';
|
||||
var frontend_server = process.env['REACT_APP_FRONTEND_SERVER'] != null ? process.env['REACT_APP_FRONTEND_SERVER'] : 'https://openmhz.com';
|
||||
var socket_server = process.env['REACT_APP_SOCKET_SERVER'] != null ? process.env['REACT_APP_SOCKET_SERVER'] : 'wss://socket.openmhz.com'; //'https://s3.amazonaws.com/robotastic';
|
||||
var account_server = process.env['REACT_APP_ACCOUNT_SERVER'] != null ? process.env['REACT_APP_ACCOUNT_SERVER'] : 'https://account.openmhz.com'; //'https://s3.amazonaws.com/robotastic';
|
||||
var dev_server = frontend_server + ":3000"
|
||||
|
||||
module.exports = function(app) {
|
||||
app.set("port", 3000)
|
||||
|
||||
// X-Powered-By header has no functional value.
|
||||
// Keeping it makes it easier for an attacker to build the site's profile
|
||||
// It can be removed safely
|
||||
app.disable("x-powered-by")
|
||||
app.enable('trust proxy')
|
||||
|
||||
app.use(express.static(path.join(process.cwd(), 'public')));
|
||||
app.set('view engine', 'ejs')
|
||||
app.set('views', './')
|
||||
|
||||
var node_env = process.env.NODE_ENV;
|
||||
console.log('--------------------------');
|
||||
console.log('===> 😊 Starting Server . . .');
|
||||
console.log('===> Environment: ' + node_env);
|
||||
if(node_env === 'production') {
|
||||
console.log('===> 🚦 Note: In order for authentication to work in production');
|
||||
console.log('===> you will need a secure HTTPS connection');
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
app.use('/*', function(req, res, next) {
|
||||
var allowedOrigins = [ account_server];
|
||||
allowedOrigins.push(frontend_server);
|
||||
allowedOrigins.push(backend_server);
|
||||
allowedOrigins.push(socket_server);
|
||||
allowedOrigins.push(dev_server);
|
||||
var origin = req.headers.origin;
|
||||
|
||||
|
||||
if (allowedOrigins.indexOf(origin) > -1) {
|
||||
res.setHeader('Access-Control-Allow-Origin', origin);
|
||||
} else if (req.headers["user-agent"] == 'TrunkRecorder1.0') {
|
||||
res.setHeader('Access-Control-Allow-Origin', "*");
|
||||
} else {
|
||||
res.setHeader('Access-Control-Allow-Origin', "*");
|
||||
if (origin) {
|
||||
console.warn("forcing CORS for: " + origin + " referer: " + req.headers.referer);
|
||||
}
|
||||
}
|
||||
res.header("Access-Control-Allow-Headers", "X-Requested-With");
|
||||
res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,OPTIONS,WEBSOCKET');
|
||||
res.header('Access-Control-Allow-Credentials', 'true');
|
||||
res.header("Access-Control-Allow-Headers", "Access-Control-Allow-Headers, Access-Control-Allow-Credentials, Origin,Accept, X-Requested-With, Content-Type, Access-Control-Request-Method, Access-Control-Request-Headers, Access-Control-Max-Age");
|
||||
res.header('Access-Control-Max-Age', '600');
|
||||
next();
|
||||
});
|
||||
|
||||
|
||||
|
||||
}
|
||||
15
frontend/server/config/secrets.js
Normal file
15
frontend/server/config/secrets.js
Normal file
@@ -0,0 +1,15 @@
|
||||
/** Important **/
|
||||
/** You should not be committing this file to GitHub **/
|
||||
/** Repeat: DO! NOT! COMMIT! THIS! FILE! TO! YOUR! REPO! **/
|
||||
var host = process.env['MONGO_NODE_DRIVER_HOST'] != null ? process.env['MONGO_NODE_DRIVER_HOST'] : 'localhost';
|
||||
var port = process.env['MONGO_NODE_DRIVER_PORT'] != null ? process.env['MONGO_NODE_DRIVER_PORT'] : 27017;
|
||||
var mongoUrl = 'mongodb://' + host + ':' + port + '/scanner';
|
||||
|
||||
|
||||
|
||||
const secrets = {
|
||||
db: mongoUrl,
|
||||
sessionSecret: "letthisbeyoursecret"
|
||||
}
|
||||
|
||||
module.exports = secrets
|
||||
39
frontend/server/db.js
Normal file
39
frontend/server/db.js
Normal file
@@ -0,0 +1,39 @@
|
||||
var MongoClient = require('mongodb').MongoClient;
|
||||
|
||||
|
||||
var host = process.env['MONGO_NODE_DRIVER_HOST'] != null ? process.env['MONGO_NODE_DRIVER_HOST'] : 'localhost';
|
||||
var port = process.env['MONGO_NODE_DRIVER_PORT'] != null ? process.env['MONGO_NODE_DRIVER_PORT'] : 27017;
|
||||
var dbUser = process.env['MONGO_TRUNK_FRONTEND_USER'] != null ? process.env['MONGO_TRUNK_FRONTEND_USER'] : null;
|
||||
var dbPass = process.env['MONGO_TRUNK_FRONTEND_PASS'] != null ? process.env['MONGO_TRUNK_FRONTEND_PASS'] : null;
|
||||
|
||||
var state = {
|
||||
db: null,
|
||||
}
|
||||
|
||||
exports.connect = function(done) {
|
||||
if (state.db) return done()
|
||||
|
||||
var url = 'mongodb://' + host + ':' + port;
|
||||
|
||||
MongoClient.connect(url, function(err, client) {
|
||||
if (err) return done(err)
|
||||
|
||||
//test.equal(true, result);
|
||||
state.db = client.db('scanner');
|
||||
done();
|
||||
})
|
||||
}
|
||||
|
||||
exports.get = function() {
|
||||
return state.db
|
||||
}
|
||||
|
||||
exports.close = function(done) {
|
||||
if (state.db) {
|
||||
state.db.close(function(err, result) {
|
||||
state.db = null
|
||||
state.mode = null
|
||||
done(err)
|
||||
})
|
||||
}
|
||||
}
|
||||
58
frontend/server/index.ejs
Normal file
58
frontend/server/index.ejs
Normal file
@@ -0,0 +1,58 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="icon" href="<%= PUBLIC_URL%>/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<meta
|
||||
name="<%= REACT_APP_SITE_NAME%>"
|
||||
content="Listen to live Police and Fire radio broadcasts. Archived for 30 days and searchable."
|
||||
/>
|
||||
<meta name="twitter:card" content="player"/>
|
||||
<meta name="twitter:site" content="@openmhz"/>
|
||||
<!--if channel
|
||||
meta(name="twitter:title" content="#{channel.alpha} #{time} #{date}")
|
||||
meta(name="twitter:description" content="This is a call from talkgroup #{channel.desc}")-->
|
||||
<meta name="twitter:image" content="https://openmhz.com/img/player.png"/>
|
||||
<meta name="twitter:player" content="https://openmhz.com/cards/<%= CALL_ID%>"/>
|
||||
<meta name="twitter:player:stream" content="<%= CALL_URL%>"/>
|
||||
<meta name="twitter:player:stream:content_type" content="audio/mp4"/>
|
||||
<meta name="twitter:player:width" content="422"/>
|
||||
<meta name="twitter:player:height" content="114"/>
|
||||
<link rel="apple-touch-icon" href="<%= PUBLIC_URL%>/apple-touch-icon.png" />
|
||||
<!--
|
||||
manifest.json provides metadata used when your web app is installed on a
|
||||
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
|
||||
-->
|
||||
<link rel="manifest" href="<%= PUBLIC_URL%>/site.webmanifest" />
|
||||
<!--
|
||||
Notice the use of %PUBLIC_URL% in the tags above.
|
||||
It will be replaced with the URL of the `public` folder during the build.
|
||||
Only files inside the `public` folder can be referenced from the HTML.
|
||||
|
||||
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
|
||||
work correctly both with client-side routing and a non-root public URL.
|
||||
Learn how to configure a non-root public URL by running `npm run build`.
|
||||
-->
|
||||
<script src="<%= REACT_APP_BACKEND_SERVER%>/socket.io/socket.io.js"></script>
|
||||
<script>
|
||||
window.socket = io('<%= REACT_APP_BACKEND_SERVER%>');
|
||||
</script>
|
||||
<title>%REACT_APP_SITE_NAME%</title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div id="root"></div>
|
||||
<!--
|
||||
This HTML file is a template.
|
||||
If you open it directly in the browser, you will see an empty page.
|
||||
|
||||
You can add webfonts, meta tags, or analytics to this file.
|
||||
The build step will place the bundled scripts into the <body> tag.
|
||||
|
||||
To begin the development, run `npm start` or `yarn start`.
|
||||
To create a production bundle, use `npm run build` or `yarn build`.
|
||||
-->
|
||||
</body>
|
||||
</html>
|
||||
146
frontend/server/index.js
Normal file
146
frontend/server/index.js
Normal file
@@ -0,0 +1,146 @@
|
||||
var ObjectID = require('mongodb').ObjectID;
|
||||
var db = require('./db');
|
||||
const express = require("express");
|
||||
const path = require("path");
|
||||
const secrets = require("./config/secrets");
|
||||
const configureExpress = require("./config/express");
|
||||
|
||||
|
||||
// -------------------------------------------
|
||||
|
||||
const app = express()
|
||||
|
||||
// -------------------------------------------
|
||||
|
||||
// -------------------------------------------
|
||||
|
||||
// Connect to Mongo on start
|
||||
db.connect(function (err) {
|
||||
if (err) {
|
||||
console.log('Unable to connect to Mongo.')
|
||||
process.exit(1)
|
||||
}
|
||||
})
|
||||
// -------------------------------------------
|
||||
|
||||
const isDev = process.env.NODE_ENV === "development"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// -------------------------------------------
|
||||
|
||||
|
||||
configureExpress(app)
|
||||
|
||||
|
||||
function getCard(req, res, next) {
|
||||
var objectId = req.params.id;
|
||||
try {
|
||||
var o_id = ObjectID.createFromHexString(objectId);
|
||||
} catch (err) {
|
||||
console.warn("Error - /card/:id generating ObjectID " + err);
|
||||
res.status(500);
|
||||
res.send(JSON.stringify({
|
||||
error: err,
|
||||
"_id": objectId
|
||||
}));
|
||||
return;
|
||||
}
|
||||
|
||||
db.get().collection('calls', function (err, transCollection) {
|
||||
transCollection.findOne({
|
||||
'_id': o_id
|
||||
},
|
||||
function (err, item) {
|
||||
if (item) {
|
||||
var time = new Date(item.time);
|
||||
var timeString = time.toLocaleTimeString("en-US");
|
||||
var dateString = time.toDateString();
|
||||
res.render('card.ejs', {
|
||||
item: item,
|
||||
url: item.url,
|
||||
time: timeString,
|
||||
date: dateString
|
||||
});
|
||||
} else {
|
||||
console.warn("Error - /card/:id Could not find Item " + err);
|
||||
res.send(404, 'Sorry, we cannot find that!');
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
exports.get_card = function(req, res) {
|
||||
var objectId = req.params.id;
|
||||
try {
|
||||
var o_id = ObjectID.createFromHexString(objectId);
|
||||
} catch (err) {
|
||||
console.warn("Error - /card/:id generating ObjectID " + err);
|
||||
res.status(500);
|
||||
res.send(JSON.stringify({
|
||||
error: err,
|
||||
"_id": objectId
|
||||
}));
|
||||
return;
|
||||
}
|
||||
db.get().collection('calls', function(err, transCollection) {
|
||||
transCollection.findOne({
|
||||
'_id': o_id
|
||||
},
|
||||
function(err, item) {
|
||||
//console.log(util.inspect(item));
|
||||
if (item) {
|
||||
var time = new Date(item.time);
|
||||
var timeString = time.toLocaleTimeString("en-US");
|
||||
var dateString = time.toDateString();
|
||||
res.render('card', {
|
||||
item: item,
|
||||
channel: channels[item.talkgroupNum],
|
||||
time: timeString,
|
||||
date: dateString
|
||||
});
|
||||
} else {
|
||||
console.warn("Error - /card/:id Could not find Item " + err);
|
||||
res.send(404, 'Sorry, we cannot find that!');
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
*/
|
||||
|
||||
function getCalls(req, res, next) {
|
||||
for (const key in req.query) {
|
||||
console.log(key, req.query[key])
|
||||
}
|
||||
if (req.query && req.query["call-id"]) {
|
||||
const callId = req.query["call-id"];
|
||||
res.render("index.ejs", { PUBLIC_URL: "/", REACT_APP_SITE_NAME: process.env["REACT_APP_SITE_NAME"], REACT_APP_BACKEND_SERVER: process.env["REACT_APP_BACKEND_SERVER"], CALL_ID: callId, CALL_URL: "https://s3.us-west-1.wasabisys.com/openmhz-west/media/dcfd-1039-1613917169.m4a" })
|
||||
} else {
|
||||
next()
|
||||
}
|
||||
|
||||
}
|
||||
// -------------------------------------------
|
||||
app.use(express.static(path.join(__dirname, "public")));
|
||||
|
||||
app.get("/systems/:shortName", getCalls)
|
||||
app.get("/cards/:id", getCard)
|
||||
|
||||
app.get("*", (req, res, next) => {
|
||||
res.sendFile(__dirname + '/public/index.html');
|
||||
});
|
||||
|
||||
// start listening to incoming requests
|
||||
app.listen(app.get("port"), app.get("host"), (err) => {
|
||||
if (err) {
|
||||
console.err(err.stack)
|
||||
} else {
|
||||
console.log(`App listening on port ${app.get("port")} [${process.env.NODE_ENV} mode]`)
|
||||
}
|
||||
})
|
||||
Reference in New Issue
Block a user