mirror of
https://github.com/openmhz/trunk-server.git
synced 2026-11-12 08:47:40 -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) {
|
||||
res.status(401);
|
||||
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.get("/systems", isLoggedIn, systems.listSystems)
|
||||
app.get("/admin/systems", isAdmin, systems.listAllSystems)
|
||||
app.get("/admin/users/active", isAdmin, systems.listActiveUsers)
|
||||
app.get("/admin/users", isAdmin, systems.listUserSystems)
|
||||
app.delete("/systems/:shortName", isLoggedIn, systems.deleteSystem)
|
||||
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 UpdateSystem from "../System/UpdateSystem"
|
||||
import CreateSystem from "../System/CreateSystem"
|
||||
import ActiveUsers from "../ActiveUsers/ActiveUsers"
|
||||
|
||||
|
||||
const App = (params) => {
|
||||
@@ -22,6 +23,7 @@ const App = (params) => {
|
||||
<Route path="/create-system" element={ <Restricted><CreateSystem/></Restricted>} />
|
||||
<Route path="/list-systems" element={ <Restricted><ListSystems/></Restricted>} />
|
||||
<Route path="/all-systems" element={ <AdminRestricted><AllSystems/></AdminRestricted>} />
|
||||
<Route path="/active-users" element={ <AdminRestricted><ActiveUsers/></AdminRestricted>} />
|
||||
<Route path="/" element={ <Restricted><ListSystems/></Restricted>} />
|
||||
<Route render={() => (<div>Miss</div>)} />
|
||||
</Routes>
|
||||
|
||||
@@ -24,6 +24,10 @@ const Navigation = (props) => {
|
||||
{process.env.REACT_APP_SITE_NAME}
|
||||
</Menu.Item>
|
||||
<Menu.Menu position="right">
|
||||
{admin &&
|
||||
<Link to="/active-users">
|
||||
<Menu.Item name="active-users">Active Users</Menu.Item>
|
||||
</Link> }
|
||||
{admin &&
|
||||
<Link to="/all-systems">
|
||||
<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'
|
||||
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({
|
||||
query: (system) => ({
|
||||
url: `/systems`,
|
||||
@@ -202,4 +206,4 @@ export const apiSlice = createApi({
|
||||
})
|
||||
|
||||
// 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 { 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 [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 (
|
||||
<div>
|
||||
<Menu fixed="top">
|
||||
@@ -11,30 +29,32 @@ const AboutComponent = () => {
|
||||
<Link to="/systems"><Menu.Item link>Listen</Menu.Item></Link>
|
||||
</Menu>
|
||||
<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
|
||||
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>
|
||||
<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
|
||||
lead to greater appreciation for all the work they do that goes largely unseen. I am personally amazed by the number
|
||||
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>
|
||||
lead to a greater appreciation for all the work they do, which goes largely unseen.</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
|
||||
at the end of the month.</p>
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
<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? 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>
|
||||
<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><a href="https://github.com/sponsors/robotastic"><Button>
|
||||
<p><a href="https://github.com/sponsors/robotastic"><Button color='green'>
|
||||
<Icon name='heart' />
|
||||
Support
|
||||
</Button></a></p>
|
||||
|
||||
<Divider horizontal style={{ paddingTop: "4em", paddingBottom: "2em" }}><Header as="h2">FAQ</Header></Divider>
|
||||
<Accordion defaultActiveIndex={0} panels={panels} />
|
||||
<Divider/>
|
||||
</Container>
|
||||
</div>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user