From 82f96493ceb065ad25cab5c32dd3e9fb44bd78ea Mon Sep 17 00:00:00 2001 From: rdbsolutions Date: Tue, 12 Aug 2025 12:49:42 +0100 Subject: [PATCH 1/2] storage account report --- actions/azurestorage.js | 42 ++++++++++++++++++ pages/storageAdmin.js | 94 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 136 insertions(+) create mode 100644 pages/storageAdmin.js diff --git a/actions/azurestorage.js b/actions/azurestorage.js index ee392b9f..aa5617bc 100644 --- a/actions/azurestorage.js +++ b/actions/azurestorage.js @@ -1693,3 +1693,45 @@ export const listBlobHierarchical = async ( } } }; +export async function listBlobHierarchicalForUser(containerClient) { + const blobNames = []; + for await (const blob of containerClient.listBlobsFlat()) { + blobNames.push(blob.name); + } + return blobNames; +} + +export async function listContainersForUser( + blobServiceClient, + containerNamePrefix, + emailAddress +) { + const options = { + includeDeleted: false, + includeMetadata: true, + includeSystem: true, + prefix: containerNamePrefix, + }; + + const results = []; + + for await (const containerItem of blobServiceClient.listContainers( + options + )) { + const containerClient = blobServiceClient.getContainerClient( + containerItem.name + ); + const blobNames = await listBlobHierarchicalForUser( + containerClient + ).catch(console.error); + + if (blobNames && blobNames.length > 0) { + results.push({ + container: containerItem.name, + blobs: blobNames, + }); + } + } + + return results; // array of non-empty containers +} diff --git a/pages/storageAdmin.js b/pages/storageAdmin.js new file mode 100644 index 00000000..77cd088b --- /dev/null +++ b/pages/storageAdmin.js @@ -0,0 +1,94 @@ +import { DefaultAzureCredential } from "@azure/identity"; +import { BlobServiceClient, ContainerClient } from "@azure/storage-blob"; +import { v4 as uuidv4 } from "uuid"; +import { + createContainerSas, + listContainers, + listBlobHierarchical, + listContainersForUser, +} from "../actions/azurestorage"; + +import { PrismaClient } from "@prisma/client"; + +import { consoleLogger, getIP } from "../actions"; + +export async function getServerSideProps({ req }) { + // Load allowed IPs from env and trim spaces + const ALLOWED_IPS = process.env.ALLOWED_IPS + ? process.env.ALLOWED_IPS.split(",").map((ip) => ip.trim()) + : []; + + // Always allow localhost addresses + const LOCALHOST_IPS = ["127.0.0.1", "::1"]; + + // Get IP address from headers or socket + const forwarded = req.headers["x-forwarded-for"]; + const ip = + typeof forwarded === "string" + ? forwarded.split(",")[0] + : req.socket.remoteAddress; + + console.log("Visitor IP:", ip); + + // Check whitelist + localhost + if (![...ALLOWED_IPS, ...LOCALHOST_IPS].includes(ip)) { + return { + redirect: { + destination: "/403", // custom "Access Denied" page + permanent: false, + }, + }; + } + const accountName = process.env.AZURE_STORAGE_ACCOUNT_NAME; + + const creds = new DefaultAzureCredential(); + + const globalForPrisma = global; + + const prisma = globalForPrisma.prisma || new PrismaClient(); + + const blobServiceClient = new BlobServiceClient( + `https://${accountName}.blob.core.windows.net`, + creds + ); + + const users = await prisma.user.findMany(); + + const data = await Promise.all( + users.map(async (user) => { + const containers = await listContainersForUser( + blobServiceClient, + `${user.id}`, + user.email + ); + return containers.length > 0 + ? { id: user.id, email: user.email, containers } + : null; + }) + ); + + return { props: { data: data.filter(Boolean) } }; +} +export default function StoragePage({ data }) { + return ( +
+

Storage Account Contents

+ {data.map((user) => ( +
+ {user.containers.map((c) => ( +
+

+ {c.container} - {user.email} +

+
    + {c.blobs.map((blob) => ( +
  • {blob}
  • + ))} +
+
+ ))} +
+ ))} +
+ ); +} From 21bba4a131fba7a1bfdc3f6ab41c91d4951017af Mon Sep 17 00:00:00 2001 From: rdbsolutions Date: Tue, 12 Aug 2025 12:52:43 +0100 Subject: [PATCH 2/2] use ticketnumber not ref/title/pinswg_name --- components/case/representation/index.js | 2 ++ .../case/representation/representationLPACapacitySelection.js | 1 + components/case/representation/representationLPADetails.js | 4 ++-- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/components/case/representation/index.js b/components/case/representation/index.js index fe53112c..fcba40bf 100644 --- a/components/case/representation/index.js +++ b/components/case/representation/index.js @@ -62,6 +62,7 @@ let MakeRepresentation = (props) => { showQuestionnaireSection, initialValues, setCurrentReference, + ticketnumber, } = props; const formObj = props.props.form; let setCaseQueryObj = props[currentType] || {}; @@ -458,6 +459,7 @@ let MakeRepresentation = (props) => { setSubmitBack={setSubmitBack} props={props} caseReference={caseReference} + ticketnumber={ticketnumber} setRepFileName={setRepFileName} updateRepresentation={updateRepresentation} setSavingStatus={setSavingStatus} diff --git a/components/case/representation/representationLPACapacitySelection.js b/components/case/representation/representationLPACapacitySelection.js index bfc2260e..47122ad9 100644 --- a/components/case/representation/representationLPACapacitySelection.js +++ b/components/case/representation/representationLPACapacitySelection.js @@ -34,6 +34,7 @@ let RepLPACapacitySelection = (props) => { casesObj={casesObj} detailsObj={detailsObj} caseReference={props.props.caseReference} + ticketnumber={props.props.ticketnumber} searchResultsObj={props.props.searchResultsObj} props={props.props} />{" "} diff --git a/components/case/representation/representationLPADetails.js b/components/case/representation/representationLPADetails.js index 55c19618..1015f95e 100644 --- a/components/case/representation/representationLPADetails.js +++ b/components/case/representation/representationLPADetails.js @@ -8,7 +8,7 @@ const RepLPADetails = (props) => { const router = useRouter(); const { locale } = router; - const { currentType, casesObj, caseReference } = props; + const { currentType, casesObj, caseReference, ticketnumber } = props; let setCaseDetailsObj = router.query.hasOwnProperty("state") ? router.query.state == "edit" @@ -21,7 +21,7 @@ const RepLPADetails = (props) => { var detailsObj = {}; detailsObj = jsonpath({ - path: '$..[?(@ && @.pinswg_name=="' + caseReference + '")]', + path: '$..[?(@ && @.ticketnumber=="' + ticketnumber + '")]', json: setCaseDetailsObj, eval: true, });