Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add standalone mode for local scans (WIP) #442

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion public/components/navigation/navigation.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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");
}
}
}
2 changes: 2 additions & 0 deletions public/components/searchbar/searchbar.css
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,7 @@ div.search-result-pannel .package+.package {
height: 30px;
left: 50px;
padding-left: 20px;
min-width: 455px;
max-width: calc(100vw - 70px);
box-sizing: border-box;
background: var(--primary);
Expand All @@ -227,6 +228,7 @@ div.search-result-pannel .package+.package {
display: flex;
max-width: calc(100vw - 70px - 264px);
background: var(--primary);
margin-left: auto;
}

#search-nav .packages>.package {
Expand Down
30 changes: 28 additions & 2 deletions public/core/search-nav.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,20 @@ import { createDOMElement, parseNpmSpec } from "../common/utils";
import { SearchBar } from "../components/searchbar/searchbar";

export function initSearchNav(data, options) {
const { initFromZero = true, searchOptions = null } = options;
const { initFromZero = true, searchOptions = null, initSinglePackage } = options;

const searchNavElement = document.getElementById("search-nav");
if (!searchNavElement) {
throw new Error("Unable to found search navigation");
}

if (initFromZero) {
if (initSinglePackage) {
searchNavElement.innerHTML = "";
searchNavElement.appendChild(
initSingleNavigation(initSinglePackage)
);
}
else if (initFromZero) {
searchNavElement.innerHTML = "";
searchNavElement.appendChild(
initPackagesNavigation(data)
Expand Down Expand Up @@ -122,3 +128,23 @@ function renderPackageRemoveButton(packageName, options) {

return removeButton;
}

function initSingleNavigation(packageName) {
const fragment = document.createDocumentFragment();
const container = createDOMElement("div", {
classList: ["packages"]
});

const pkgElement = createDOMElement("div", {
classList: ["package"],
childs: [
createDOMElement("p", { text: packageName })
]
});
pkgElement.dataset.name = packageName;

container.appendChild(pkgElement);
fragment.append(container);

return fragment;
}
19 changes: 17 additions & 2 deletions public/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,13 @@ 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 });
}
window.i18n = await new i18n().fetch();
window.navigation = new ViewNavigation();
window.wiki = new Wiki();

await init();
Expand Down Expand Up @@ -109,7 +113,18 @@ 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) {
window.activePackage = secureDataSet.data.rootDependencyName;
initSearchNav(void 0, {
initSinglePackage: secureDataSet.data.rootDependencyName,
searchOptions: {
nsn, secureDataSet
}
});
}
else {
searchview ??= new SearchView(secureDataSet, nsn);
}

window.addEventListener("package-info-closed", () => {
window.networkNav.currentNodeParams = null;
Expand Down
13 changes: 11 additions & 2 deletions src/commands/scanner.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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"));

Expand All @@ -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;
}
22 changes: 22 additions & 0 deletions src/http-server/cache.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 });
}
Expand Down Expand Up @@ -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();
23 changes: 20 additions & 3 deletions src/http-server/config.js
Original file line number Diff line number Diff line change
@@ -1,25 +1,42 @@
// 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
const kDefaultConfig = {
defaultPackageMenu: "info",
ignore: { flags: [], warnings: [] }
ignore: { flags: [], warnings: [] },
standalone: false
};

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: {
flags,
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;
}
Expand Down
8 changes: 8 additions & 0 deletions src/http-server/endpoints/data.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,14 @@ import { logger } from "../logger.js";
const kDefaultPayloadPath = path.join(process.cwd(), "nsecure-result.json");

export async function get(_req, res) {
if (appCache.isStandalone) {
logger.info("[data|get] standalone mode");
const payload = await appCache.getStandalonePayload();
send(res, 200, payload);

return;
}

try {
const { current, lru } = await appCache.payloadsList();
logger.info(`[data|get](current: ${current})`);
Expand Down
4 changes: 4 additions & 0 deletions src/http-server/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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)})`);
Expand Down
4 changes: 4 additions & 0 deletions src/http-server/websocket/init.js
Original file line number Diff line number Diff line change
Expand Up @@ -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})`);
Expand Down
7 changes: 4 additions & 3 deletions test/config.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,22 +29,23 @@ describe("config", { concurrency: 1 }, () => {

assert.deepStrictEqual(value, {
defaultPackageMenu: "info",
ignore: { flags: [], warnings: [] }
ignore: { flags: [], warnings: [] },
standalone: false
});
});

it("should get config from cache", async() => {
await cacache.put(CACHE_PATH, kConfigKey, JSON.stringify({ foo: "bar" }));
const value = await get();

assert.deepStrictEqual(value, { foo: "bar" });
assert.deepStrictEqual(value, { foo: "bar", standalone: false });
});

it("should set config in cache", async() => {
await set({ foo: "baz" });
const value = await get();

assert.deepStrictEqual(value, { foo: "baz" });
assert.deepStrictEqual(value, { foo: "baz", standalone: false });
});
});

4 changes: 2 additions & 2 deletions test/httpServer.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -212,10 +212,10 @@ describe("httpServer", { concurrency: 1 }, () => {
test("GET '/config' should return the config", async() => {
const { data: actualConfig } = await get(new URL("/config", HTTP_URL));

await cacache.put(CACHE_PATH, kConfigKey, JSON.stringify({ foo: "bar" }));
await cacache.put(CACHE_PATH, kConfigKey, JSON.stringify({ foo: "bar", standalone: false }));
const result = await get(new URL("/config", HTTP_URL));

assert.deepEqual(result.data, { foo: "bar" });
assert.deepEqual(result.data, { foo: "bar", standalone: false });

await fetch(new URL("/config", HTTP_URL), {
method: "PUT",
Expand Down
4 changes: 3 additions & 1 deletion workspaces/vis-network/src/dataset.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Loading