diff --git a/client/src/pages/makerspace_page/ExpandableHeader.tsx b/client/src/pages/makerspace_page/ExpandableHeader.tsx index 1f3a8deb..74b6a3c8 100644 --- a/client/src/pages/makerspace_page/ExpandableHeader.tsx +++ b/client/src/pages/makerspace_page/ExpandableHeader.tsx @@ -5,7 +5,7 @@ import { isManagerFor } from "../../common/PrivilegeUtils"; import EditIcon from "@mui/icons-material/Edit"; import KeyboardArrowUpIcon from "@mui/icons-material/KeyboardArrowUp"; import { FullMakerspace } from "../../queries/makerspaceQueries"; -import { useNavigate } from "react-router-dom"; +import { useNavigate, useSearchParams } from "react-router-dom"; import { useCurrentUser } from "../../common/CurrentUserProvider"; import MakerspaceHours from "../../types/MakerspaceHours"; import MakerspaceHoursSection from "./MakerspaceHours"; @@ -127,10 +127,12 @@ export default function ExpandableHeader({ makerspace, makerspaceTrainings }: Ex const navigate = useNavigate(); const isMobile = useIsMobile(); - const [expanded, setExpanded] = useState(user.visitor); + const [searchParams, setSearchParams] = useSearchParams() + const startOpen = searchParams.get("o") == "1" + const [expanded, setExpanded] = useState(user.visitor || startOpen); return ( - setExpanded(!expanded)}> + {setExpanded(!expanded); setSearchParams({"o":(!expanded) ? "1" : "0"})}}> {TitleRow( navigate, diff --git a/client/src/pages/makerspace_page/device_pages/DevicesPage.tsx b/client/src/pages/makerspace_page/device_pages/DevicesPage.tsx index 87ca140b..09cb5641 100644 --- a/client/src/pages/makerspace_page/device_pages/DevicesPage.tsx +++ b/client/src/pages/makerspace_page/device_pages/DevicesPage.tsx @@ -38,7 +38,7 @@ export default function DevicesPage() { const cores: Core[] = makerspaceWithDevicesResult.data?.makerspaceByID.cores ?? []; const _dispensers: Dispenser[] = makerspaceWithDevicesResult.data?.makerspaceByID.dispensers ?? []; - const filteredCores: Core[] = cores.filter((core) => core.device.name.includes(searchText)) + const filteredCores: Core[] = cores.filter((core) => core.device.name.toLowerCase().includes(searchText.toLowerCase())) return ( diff --git a/server/src/graphql/resolvers/maintenanceTicketResolver.ts b/server/src/graphql/resolvers/maintenanceTicketResolver.ts index 94f246d3..04ae8262 100644 --- a/server/src/graphql/resolvers/maintenanceTicketResolver.ts +++ b/server/src/graphql/resolvers/maintenanceTicketResolver.ts @@ -165,18 +165,20 @@ const MaintenanceTicketResolver = { if (args.assignedID) { const assigned = await UserRepo.getUserByID(args.assignedID); await AuditLogRepo.createAuditLog( - `{user} assigned ticket #${args.id} to {user}`, + `{user} assigned ticket #${args.id} for {equipment} to {user}`, "admin", makerspaceID, { id: user.id, label: UserRepo.getUsersFullName(user) }, + { id: equipRow?.id ?? 0, label: equipRow?.name ?? "unknown equipment" }, { id: assigned.id, label: UserRepo.getUsersFullName(assigned) } ); } else { await AuditLogRepo.createAuditLog( - `{user} unassigned ticket #${args.id}`, + `{user} unassigned ticket #${args.id} for {equipment}`, "admin", makerspaceID, - { id: user.id, label: UserRepo.getUsersFullName(user) } + { id: user.id, label: UserRepo.getUsersFullName(user) }, + { id: equipRow?.id ?? 0, label: equipRow?.name ?? "unknown equipment" } ); } }), diff --git a/server/src/server.ts b/server/src/server.ts index 14e8f9bf..33d60035 100644 --- a/server/src/server.ts +++ b/server/src/server.ts @@ -16,7 +16,7 @@ import path from "path"; import * as schedule from "node-schedule"; import { getUserByCardTagID, getUsersFullName } from "./database/repositories/Users/UserRepository.js"; import { createUnassocaitedAuditLog } from "./database/repositories/AuditLogs/AuditLogRepository.js"; -import { getReaderCertCA } from "./database/repositories/Readers/ReaderRepository.js"; +import { getReaderCertCA, setReaderCertCA } from "./database/repositories/Readers/ReaderRepository.js"; import morgan from "morgan"; //Log provider import { createRequire } from "module"; import { setDataPointValue } from "./database/repositories/DataPoints/DataPointsRepository.js"; @@ -43,10 +43,10 @@ import fs from "node:fs"; import { ViteDevServer } from "vite"; import { SiteSettings } from "./database/models/site_settings/SiteSettings.js"; import * as ThemeRepo from "./database/repositories/SiteSettings/ThemesRepository.js"; +import { createHash } from 'node:crypto'; const require = createRequire(import.meta.url); -const allowed_origins = [process.env.VITE_ORIGIN, "https://studio.apollographql.com", "https://make.rit.edu", "https://shibboleth.main.ad.rit.edu"]; const SECURE_ORIGIN = (process.env.VITE_ORIGIN ?? ""); const __dirname = import.meta.dirname; @@ -249,12 +249,32 @@ async function startServer() { }); app.use("/api/files/", express.static(path.join(__dirname, '../../client/shlug-files/'))); - app.get("/api/files/certCA", async function (req, res) { + app.get("/api/rootCA", async function (req, res) { + const SNHeader = 'shlug-sn'; + if (!req.headers[SNHeader]) { + return res.status(401).send(); + } + const SN = req.headers[SNHeader]; + if (typeof SN !== "string") { + return res.status(401).send(); + } + + const device = await getDeviceBySN(SN); + if (device == null) { + return res.status(404).send(); + } + const certca = (await getReaderCertCA())?.value; if (certca == null) { return res.status(404).send(); } - return res.send(certca); + const textForSha = `${device.SN}:${await device.generateKey()}:${certca}` + const sha = createHash('sha256').update(textForSha).digest('hex') + const result = { + cert: certca, + sha: sha, + } + return res.json(result); }) app.get('/api/files/ota/:tagname', async function (req, res) { @@ -542,6 +562,27 @@ async function startServer() { createUnassocaitedAuditLog(`Trainings: Sent ${numNotified} expiry notices, and purged ${numPurged} expired trainings.`, "server") } + async function updateRootCert() { + try { + const url = process.env.READER_CERT_URL; + if (url == undefined || url == "") { + console.error("Can not update root cert. No download URL provided"); + return + } + const response = await fetch(url); + + if (!response.ok) { + console.error(`Could not download new root cert. HTTP error: ${response.status}`) + return; + } + + const certString = await response.text(); + // normalize \r\n to \n + setReaderCertCA(certString.replace(/\r/g,"")); + } catch (error) { + console.error(`Failed to update root cert: ${error}`) + } + } /** Cron Format: * * * * * * @@ -574,6 +615,9 @@ async function startServer() { // Advance any time-based maintennace tickets from UPCOMING -> TODO await advanceTimeTickets(); + // Get a new root certificate for readers + await updateRootCert(); + // Command all cores to restart for the periodic restart await scheduledRestartAllCores(); });