From 1c32b6963c02f91d7f2e857709610120d4890cc0 Mon Sep 17 00:00:00 2001 From: Pierre Demailly Date: Sun, 5 Jan 2025 20:06:18 +0100 Subject: [PATCH] feat: add standalone mode for local scans (WIP) --- public/components/navigation/navigation.js | 17 ++++++++++++++++- public/core/search-nav.js | 7 +++++++ public/main.js | 13 ++++++++++--- src/commands/scanner.js | 13 +++++++++++-- src/http-server/cache.js | 22 ++++++++++++++++++++++ src/http-server/config.js | 20 ++++++++++++++++++-- src/http-server/index.js | 4 ++++ src/http-server/websocket/init.js | 4 ++++ workspaces/vis-network/src/dataset.js | 4 +++- 9 files changed, 95 insertions(+), 9 deletions(-) diff --git a/public/components/navigation/navigation.js b/public/components/navigation/navigation.js index 4dead174..ba9c7fde 100644 --- a/public/components/navigation/navigation.js +++ b/public/components/navigation/navigation.js @@ -75,7 +75,7 @@ export class ViewNavigation { const searchbar = document.getElementById("searchbar"); if (searchbar) { - searchbar.style.display = menuName === "network--view" ? "flex" : "none"; + searchbar.style.display = menuName === "network--view" ? "flex" : "none"; } this.activeMenu = selectedNav; @@ -114,4 +114,19 @@ export class ViewNavigation { this.setNewActiveMenu(selectedNav); } } + + hideMenu(menuName, options = {}) { + const { navigateAway = false } = options; + + const menu = this.menus.get(menuName); + if (!menu) { + return; + } + + menu.classList.add("hidden"); + + if (navigateAway && this.activeMenu.isEqualNode(menu)) { + this.setNavByName("network--view"); + } + } } diff --git a/public/core/search-nav.js b/public/core/search-nav.js index 58fe08a1..bde4b947 100644 --- a/public/core/search-nav.js +++ b/public/core/search-nav.js @@ -122,3 +122,10 @@ function renderPackageRemoveButton(packageName, options) { return removeButton; } + +export function hideSearchNav() { + const searchNavElement = document.getElementById("search-nav"); + if (searchNavElement) { + searchNavElement.classList.add("hidden"); + } +} diff --git a/public/main.js b/public/main.js index 774cda3c..10d858cb 100644 --- a/public/main.js +++ b/public/main.js @@ -17,7 +17,7 @@ import { SearchView } from "./components/views/search/search.js"; // Import Core Components import { NetworkNavigation } from "./core/network-navigation.js"; import { i18n } from "./core/i18n.js"; -import { initSearchNav } from "./core/search-nav.js"; +import { initSearchNav, hideSearchNav } from "./core/search-nav.js"; // Import Utils import * as utils from "./common/utils.js"; @@ -31,9 +31,14 @@ document.addEventListener("DOMContentLoaded", async() => { window.scannedPackageCache = []; window.locker = null; window.popup = new Popup(); + window.navigation = new ViewNavigation(); window.settings = await new Settings().fetchUserConfig(); + if (window.settings.config.standalone) { + console.log(`[INFO] Standalone mode activated`); + window.navigation.hideMenu("search--view", { navigateAway: true }); + hideSearchNav(); + } window.i18n = await new i18n().fetch(); - window.navigation = new ViewNavigation(); window.wiki = new Wiki(); await init(); @@ -109,7 +114,9 @@ async function init(options = {}) { window.locker = new Locker(nsn); window.legend = new Legend({ show: window.settings.config.showFriendlyDependencies }); new HomeView(secureDataSet, nsn); - searchview ??= new SearchView(secureDataSet, nsn); + if (window.settings.config.standalone === false) { + searchview ??= new SearchView(secureDataSet, nsn); + } window.addEventListener("package-info-closed", () => { window.networkNav.currentNodeParams = null; diff --git a/src/commands/scanner.js b/src/commands/scanner.js index 01536dba..556b7be3 100644 --- a/src/commands/scanner.js +++ b/src/commands/scanner.js @@ -13,6 +13,7 @@ import * as Scanner from "@nodesecure/scanner"; // Import Internal Dependencies import * as http from "./http.js"; +import { appCache } from "../http-server/cache.js"; export async function auto(spec, options) { const { keep, ...commandOptions } = options; @@ -59,7 +60,7 @@ export async function cwd(options) { initLogger(void 0, !silent) ); - return logAndWrite(payload, output); + return logAndWrite(payload, output, { lockUi: true }); } export async function from(spec, options) { @@ -153,7 +154,11 @@ function initLogger(spec, verbose = true) { return logger; } -function logAndWrite(payload, output = "nsecure-result") { +function logAndWrite(payload, output = "nsecure-result", options = {}) { + const { + lockUi = false + } = options; + if (payload === null) { console.log(i18n.getTokenSync("cli.no_dep_to_proceed")); @@ -179,5 +184,9 @@ function logAndWrite(payload, output = "nsecure-result") { console.log(kleur.white().bold(i18n.getTokenSync("cli.successfully_written_json", kleur.green().bold(filePath)))); console.log(""); + if (lockUi) { + appCache.setStandalonePayload(payload); + } + return filePath; } diff --git a/src/http-server/cache.js b/src/http-server/cache.js index c2b9a753..f8324fdf 100644 --- a/src/http-server/cache.js +++ b/src/http-server/cache.js @@ -19,6 +19,13 @@ export const CACHE_PATH = path.join(os.tmpdir(), "nsecure-cli"); export const DEFAULT_PAYLOAD_PATH = path.join(process.cwd(), "nsecure-result.json"); class _AppCache { + /** + * - `undefined`: unknown + * - `true`: standalone + * - `false`: not standalone + */ + isStandalone; + constructor() { fs.mkdirSync(kPayloadsPath, { recursive: true }); } @@ -127,6 +134,21 @@ class _AppCache { root }; } + + async setStandalonePayload(payload) { + await cacache.put(CACHE_PATH, "standalone", JSON.stringify(payload)); + } + + async getStandalonePayload() { + try { + const { data } = await cacache.get(CACHE_PATH, "standalone"); + + return JSON.parse(data.toString()); + } + catch { + return null; + } + } } export const appCache = new _AppCache(); diff --git a/src/http-server/config.js b/src/http-server/config.js index 4f0998d5..42371802 100644 --- a/src/http-server/config.js +++ b/src/http-server/config.js @@ -1,5 +1,8 @@ +// Import Node.js Dependencies +import fs from "node:fs"; + // Import Internal Dependencies -import { appCache } from "./cache.js"; +import { appCache, DEFAULT_PAYLOAD_PATH } from "./cache.js"; import { logger } from "./logger.js"; // CONSTANTS @@ -12,6 +15,17 @@ export async function get() { try { const config = await appCache.getConfig(); + let standalone = false; + const standalonePayload = await appCache.getStandalonePayload(); + if (standalonePayload && fs.existsSync(DEFAULT_PAYLOAD_PATH)) { + const localPayload = JSON.parse(fs.readFileSync(DEFAULT_PAYLOAD_PATH, "utf-8")); + if (localPayload.id === standalonePayload.id) { + standalone = true; + } + } + appCache.isStandalone = standalone; + Object.assign(config, { standalone }); + const { defaultPackageMenu, ignore: { @@ -19,7 +33,9 @@ export async function get() { warnings } = {} } = config; - logger.info(`[config|get](defaultPackageMenu: ${defaultPackageMenu}|ignore-flag: ${flags}|ignore-warnings: ${warnings})`); + logger.info( + `[config|get](defaultPackageMenu: ${defaultPackageMenu}|flags: ${flags}|warnings: ${warnings}|standalone: ${standalone})` + ); return config; } diff --git a/src/http-server/index.js b/src/http-server/index.js index bab1b82d..ae240a31 100644 --- a/src/http-server/index.js +++ b/src/http-server/index.js @@ -22,6 +22,7 @@ import * as report from "./endpoints/report.js"; import * as middleware from "./middleware.js"; import * as wsHandlers from "./websocket/index.js"; import { logger } from "./logger.js"; +import { appCache } from "./cache.js"; export function buildServer(dataFilePath, options = {}) { const httpConfigPort = typeof options.port === "number" ? options.port : 0; @@ -65,6 +66,9 @@ export function buildServer(dataFilePath, options = {}) { if (enableWS) { const websocket = new WebSocketServer({ port: 1338 }); websocket.on("connection", async(socket) => { + if (appCache.isStandalone) { + return; + } socket.on("message", async(rawMessage) => { const message = JSON.parse(rawMessage); logger.info(`[ws](message: ${JSON.stringify(message)})`); diff --git a/src/http-server/websocket/init.js b/src/http-server/websocket/init.js index fe3a84ac..db1259ca 100644 --- a/src/http-server/websocket/init.js +++ b/src/http-server/websocket/init.js @@ -3,6 +3,10 @@ import { appCache } from "../cache.js"; import { logger } from "../logger.js"; export async function init(socket, lock = false) { + if (appCache.isStandalone) { + return; + } + try { const { current, lru, older, root } = await appCache.payloadsList(); logger.info(`[ws|init](lru: ${lru}|older: ${older}|current: ${current}|root: ${root})`); diff --git a/workspaces/vis-network/src/dataset.js b/workspaces/vis-network/src/dataset.js index 82959536..9f9ebb67 100644 --- a/workspaces/vis-network/src/dataset.js +++ b/workspaces/vis-network/src/dataset.js @@ -176,7 +176,9 @@ export default class NodeSecureDataSet extends EventTarget { if (author === null) { return; } - const contributor = contributors.find((contributor) => contributor.email === author.email && contributor.npmAvatar !== null); + const contributor = contributors.find( + (contributor) => contributor?.email === author.email && contributor?.npmAvatar !== null + ); if (this.authors.has(author.name)) { this.authors.get(author.name).packages.add(spec);