mirror of
https://github.com/openmhz/trunk-server.git
synced 2026-11-14 09:48:10 -05:00
Merge branch 'master' into feature/contact
This commit is contained in:
@@ -13,7 +13,44 @@ exports.isLoggedIn = function (req, res, next) {
|
|||||||
|
|
||||||
// -------------------------------------------
|
// -------------------------------------------
|
||||||
|
|
||||||
exports.listUserSystems = async function (req, res, next) {
|
|
||||||
|
exports.listActiveUsers = async function (req,res,next) {
|
||||||
|
if (!req.user.admin) {
|
||||||
|
res.status(401);
|
||||||
|
res.json({ success: false, message: "Not Authorized" });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let users = {};
|
||||||
|
let fromDate = new Date(Date.now() - 60 * 60 * 24 * 30 * 1000);
|
||||||
|
const systems = await System.find({lastActive: {$gte: fromDate}}).populate('userId', "screenName").catch( err => {
|
||||||
|
console.error("Error - get_systems: " + err.message);
|
||||||
|
res.json({
|
||||||
|
success: false,
|
||||||
|
message: err
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
});
|
||||||
|
for (let index = 0; index < systems.length; index++) {
|
||||||
|
const system = systems[index];
|
||||||
|
|
||||||
|
if (!users.hasOwnProperty(system.userId.toString())) {
|
||||||
|
const user = await User.findById(system.userId, "_id email firstName lastName lastLogin");
|
||||||
|
users[system.userId.toString()] = user.email;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
let list = [];
|
||||||
|
for (const id in users) {
|
||||||
|
list.push(users[id]);
|
||||||
|
}
|
||||||
|
|
||||||
|
res.json(list);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
exports.listUserSystems = async function (req,res,next) {
|
||||||
if (!req.user.admin) {
|
if (!req.user.admin) {
|
||||||
res.status(401);
|
res.status(401);
|
||||||
res.json({ success: false, message: "Not Authorized" });
|
res.json({ success: false, message: "Not Authorized" });
|
||||||
|
|||||||
@@ -153,6 +153,7 @@ app.get("/groups/:shortName/:groupId?", isLoggedIn, groups.getGroups);
|
|||||||
app.delete("/groups/:shortName/:groupId", isLoggedIn, groups.deleteGroup);
|
app.delete("/groups/:shortName/:groupId", isLoggedIn, groups.deleteGroup);
|
||||||
app.get("/systems", isLoggedIn, systems.listSystems)
|
app.get("/systems", isLoggedIn, systems.listSystems)
|
||||||
app.get("/admin/systems", isAdmin, systems.listAllSystems)
|
app.get("/admin/systems", isAdmin, systems.listAllSystems)
|
||||||
|
app.get("/admin/users/active", isAdmin, systems.listActiveUsers)
|
||||||
app.get("/admin/users", isAdmin, systems.listUserSystems)
|
app.get("/admin/users", isAdmin, systems.listUserSystems)
|
||||||
app.delete("/systems/:shortName", isLoggedIn, systems.deleteSystem)
|
app.delete("/systems/:shortName", isLoggedIn, systems.deleteSystem)
|
||||||
app.post("/systems/:shortName", [isLoggedIn, systems.ownSystem, systems.validateSystem, systems.updateSystem])
|
app.post("/systems/:shortName", [isLoggedIn, systems.ownSystem, systems.validateSystem, systems.updateSystem])
|
||||||
|
|||||||
37
admin/src/ActiveUsers/ActiveUsers.js
Normal file
37
admin/src/ActiveUsers/ActiveUsers.js
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
import {
|
||||||
|
Container,
|
||||||
|
Header,
|
||||||
|
Table,
|
||||||
|
Icon
|
||||||
|
} from "semantic-ui-react";
|
||||||
|
import { useGetActiveUsersQuery, } from '../features/api/apiSlice'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// ----------------------------------------------------
|
||||||
|
const ActiveUsers = () => {
|
||||||
|
const { data: users, isSuccess } = useGetActiveUsersQuery();
|
||||||
|
|
||||||
|
|
||||||
|
//https://stackoverflow.com/questions/36559661/how-can-i-dispatch-from-child-components-in-react-redux
|
||||||
|
//https://stackoverflow.com/questions/42597602/react-onclick-pass-event-with-parameter
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Container>
|
||||||
|
<Header as="h1">Email of Active Users</Header>
|
||||||
|
|
||||||
|
|
||||||
|
{users &&
|
||||||
|
users.map((user) =>
|
||||||
|
|
||||||
|
<span>{user}, </span>
|
||||||
|
)}
|
||||||
|
|
||||||
|
</Container>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ActiveUsers;
|
||||||
@@ -9,6 +9,7 @@ import AllSystems from "../AllSystems/AllSystems"
|
|||||||
import ListSystems from "../System/ListSystems"
|
import ListSystems from "../System/ListSystems"
|
||||||
import UpdateSystem from "../System/UpdateSystem"
|
import UpdateSystem from "../System/UpdateSystem"
|
||||||
import CreateSystem from "../System/CreateSystem"
|
import CreateSystem from "../System/CreateSystem"
|
||||||
|
import ActiveUsers from "../ActiveUsers/ActiveUsers"
|
||||||
|
|
||||||
|
|
||||||
const App = (params) => {
|
const App = (params) => {
|
||||||
@@ -22,6 +23,7 @@ const App = (params) => {
|
|||||||
<Route path="/create-system" element={ <Restricted><CreateSystem/></Restricted>} />
|
<Route path="/create-system" element={ <Restricted><CreateSystem/></Restricted>} />
|
||||||
<Route path="/list-systems" element={ <Restricted><ListSystems/></Restricted>} />
|
<Route path="/list-systems" element={ <Restricted><ListSystems/></Restricted>} />
|
||||||
<Route path="/all-systems" element={ <AdminRestricted><AllSystems/></AdminRestricted>} />
|
<Route path="/all-systems" element={ <AdminRestricted><AllSystems/></AdminRestricted>} />
|
||||||
|
<Route path="/active-users" element={ <AdminRestricted><ActiveUsers/></AdminRestricted>} />
|
||||||
<Route path="/" element={ <Restricted><ListSystems/></Restricted>} />
|
<Route path="/" element={ <Restricted><ListSystems/></Restricted>} />
|
||||||
<Route render={() => (<div>Miss</div>)} />
|
<Route render={() => (<div>Miss</div>)} />
|
||||||
</Routes>
|
</Routes>
|
||||||
|
|||||||
@@ -24,6 +24,10 @@ const Navigation = (props) => {
|
|||||||
{process.env.REACT_APP_SITE_NAME}
|
{process.env.REACT_APP_SITE_NAME}
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
<Menu.Menu position="right">
|
<Menu.Menu position="right">
|
||||||
|
{admin &&
|
||||||
|
<Link to="/active-users">
|
||||||
|
<Menu.Item name="active-users">Active Users</Menu.Item>
|
||||||
|
</Link> }
|
||||||
{admin &&
|
{admin &&
|
||||||
<Link to="/all-systems">
|
<Link to="/all-systems">
|
||||||
<Menu.Item name="all-systems">All Systems</Menu.Item>
|
<Menu.Item name="all-systems">All Systems</Menu.Item>
|
||||||
|
|||||||
@@ -140,6 +140,10 @@ export const apiSlice = createApi({
|
|||||||
// The URL for the request is '/fakeApi/posts'
|
// The URL for the request is '/fakeApi/posts'
|
||||||
query: () => ({ url: `/admin/users`, credentials: "include" })
|
query: () => ({ url: `/admin/users`, credentials: "include" })
|
||||||
}),
|
}),
|
||||||
|
getActiveUsers: builder.query({
|
||||||
|
// The URL for the request is '/fakeApi/posts'
|
||||||
|
query: () => ({ url: `/admin/users/active`, credentials: "include" })
|
||||||
|
}),
|
||||||
createSystem: builder.mutation({
|
createSystem: builder.mutation({
|
||||||
query: (system) => ({
|
query: (system) => ({
|
||||||
url: `/systems`,
|
url: `/systems`,
|
||||||
@@ -202,4 +206,4 @@ export const apiSlice = createApi({
|
|||||||
})
|
})
|
||||||
|
|
||||||
// Export the auto-generated hook for the `getPosts` query endpoint
|
// Export the auto-generated hook for the `getPosts` query endpoint
|
||||||
export const { useGetGroupsQuery, useGetSystemsQuery, useGetAllSystemsQuery, useGetTalkgroupsQuery, useGetErrorsQuery, useDeleteGroupMutation, useCreateGroupMutation, useCreateSystemMutation, useUpdateSystemMutation, useDeleteSystemMutation, useSaveGroupOrderMutation, useUpdateGroupMutation, useImportTalkgroupsMutation } = apiSlice
|
export const { useGetGroupsQuery, useGetSystemsQuery, useGetAllSystemsQuery, useGetActiveUsersQuery, useGetTalkgroupsQuery, useGetErrorsQuery, useDeleteGroupMutation, useCreateGroupMutation, useCreateSystemMutation, useUpdateSystemMutation, useDeleteSystemMutation, useSaveGroupOrderMutation, useUpdateGroupMutation, useImportTalkgroupsMutation } = apiSlice
|
||||||
|
|||||||
@@ -1,9 +1,27 @@
|
|||||||
import React from "react";
|
import React, {useState} from "react";
|
||||||
import { Link } from 'react-router-dom'
|
import { Link } from 'react-router-dom'
|
||||||
import { Container, Header, Divider, Icon, Menu, Button } from "semantic-ui-react";
|
import { Container, Header, Divider, Icon, Menu, Button, Accordion, } from "semantic-ui-react";
|
||||||
|
|
||||||
|
|
||||||
const AboutComponent = () => {
|
const AboutComponent = () => {
|
||||||
|
const [activeIndex, setActiveIndex] = useState(-1);
|
||||||
|
const handleClick = (e, titleProps) => {
|
||||||
|
const { index } = titleProps
|
||||||
|
const newIndex = activeIndex === index ? -1 : index
|
||||||
|
setActiveIndex(newIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
const panels = [
|
||||||
|
{
|
||||||
|
key: 'feeds',
|
||||||
|
title: {content: (<span style={{fontWeight: "bold"}}>Where do the different feeds come from?</span>)},
|
||||||
|
content: [
|
||||||
|
'It is a team effort! There are contributers from around country and even internationally, that are running Trunk Recorder to capture local radio systems and sending the transmissions to OpenMHz.',
|
||||||
|
].join(' '),
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Menu fixed="top">
|
<Menu fixed="top">
|
||||||
@@ -11,30 +29,32 @@ const AboutComponent = () => {
|
|||||||
<Link to="/systems"><Menu.Item link>Listen</Menu.Item></Link>
|
<Link to="/systems"><Menu.Item link>Listen</Menu.Item></Link>
|
||||||
</Menu>
|
</Menu>
|
||||||
<Container text>
|
<Container text>
|
||||||
<Divider horizontal style={{ paddingTop: "4em", paddingBottom: "2em" }}><Header as="h1">About<Icon name='compass' /></Header></Divider>
|
<Divider horizontal style={{ paddingTop: "4em", paddingBottom: "2em" }}><Header as="h1">About</Header></Divider>
|
||||||
|
|
||||||
<p>I wrote <a href="https://github.com/robotastic/trunk-recorder">Trunk Recorder</a> because I was curious
|
<p>I wrote <a href="https://github.com/robotastic/trunk-recorder">Trunk Recorder</a> because I was curious
|
||||||
about what my local fire station was up to and I put together the original version of OpenMHz because I
|
about what my local fire station was up to and I put together the original version of OpenMHz because I
|
||||||
figured other people might want to listen to the recordings too.</p>
|
figured other people might want to listen to the recordings too.</p>
|
||||||
<p>The latest version of this site makes it easy for other people running Trunk Recorder to share their recrodings.
|
<p>The latest version of this site makes it easy for other people running Trunk Recorder to share their recordings.
|
||||||
I am hoping that making it easier to listen to what our local fire, police and EMS have to go through everyday will
|
I am hoping that making it easier to listen to what our local fire, police and EMS have to go through everyday will
|
||||||
lead to greater appreciation for all the work they do that goes largely unseen. I am personally amazed by the number
|
lead to a greater appreciation for all the work they do, which goes largely unseen.</p>
|
||||||
of medical runs the DC's Fire and EMS department has to cover every day, and the shocking number of them that are ODs.</p>
|
|
||||||
|
|
||||||
<p>Each system should have a 30 day archive. This is to help make sure things don't get overloaded and make sure I don't get a crazy bill
|
<p>The audio from each system is archived for 30 days, so you can go back and listen to events you may have missed.</p>
|
||||||
at the end of the month.</p>
|
|
||||||
<p> - Luke <a href="mailto:luke@robotastic.com?Subject=OpenMHz" target="_top">luke@robotastic.com</a> </p>
|
<p> - Luke <a href="mailto:luke@robotastic.com?Subject=OpenMHz" target="_top">luke@robotastic.com</a> </p>
|
||||||
|
|
||||||
<Divider horizontal style={{ paddingTop: "4em", paddingBottom: "2em" }}><Header as="h2">Open Source</Header></Divider>
|
<Divider horizontal style={{ paddingTop: "4em", paddingBottom: "2em" }}><Header as="h2">100% Open Source</Header></Divider>
|
||||||
<p>Have changes you would like to suggest? Want to run your own server for your community? The source code for OpenMHz has been released:</p>
|
<p>Have changes you would like to suggest? Want to run your own server for your community? All of the code for OpenMHz is available as open source:</p>
|
||||||
<p><a href="https://github.com/openmhz/trunk-server"><Icon name='github' />Trunk Server</a></p>
|
<p><a href="https://github.com/openmhz/trunk-server"><Icon name='github' />Trunk Server</a></p>
|
||||||
<Divider horizontal style={{ paddingTop: "4em", paddingBottom: "2em" }}><Header as="h2">Support</Header></Divider>
|
<Divider horizontal style={{ paddingTop: "4em", paddingBottom: "2em" }}><Header as="h2">Support</Header></Divider>
|
||||||
<p>If OpenMHz brings you joy, think about becoming a supporter! It will cover hosting costs and help keep me focused on development.</p>
|
<p>If OpenMHz brings you joy, think about becoming a supporter! It will cover hosting costs and help keep me focused on development.</p>
|
||||||
|
|
||||||
<p><a href="https://github.com/sponsors/robotastic"><Button>
|
<p><a href="https://github.com/sponsors/robotastic"><Button color='green'>
|
||||||
|
<Icon name='heart' />
|
||||||
Support
|
Support
|
||||||
</Button></a></p>
|
</Button></a></p>
|
||||||
|
|
||||||
|
<Divider horizontal style={{ paddingTop: "4em", paddingBottom: "2em" }}><Header as="h2">FAQ</Header></Divider>
|
||||||
|
<Accordion defaultActiveIndex={0} panels={panels} />
|
||||||
|
<Divider/>
|
||||||
</Container>
|
</Container>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user