From 9966d6308e52e1d02823a03c825af324fa26fd24 Mon Sep 17 00:00:00 2001 From: Andrew Date: Thu, 24 Oct 2024 18:47:04 -0400 Subject: [PATCH 1/4] add iroh for dynamic analysis --- iroh.js | 245 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 245 insertions(+) create mode 100644 iroh.js diff --git a/iroh.js b/iroh.js new file mode 100644 index 0000000000..657c30a1a9 --- /dev/null +++ b/iroh.js @@ -0,0 +1,245 @@ + +const Iroh = require('iroh'); + +// Define the code to analyze +const code = ` +'use strict'; + +define('forum/category', [ + 'forum/infinitescroll', + 'share', + 'navigator', + 'topicList', + 'sort', + 'categorySelector', + 'hooks', + 'alerts', + 'api', +], function (infinitescroll, share, navigator, topicList, sort, categorySelector, hooks, alerts, api) { + const Category = {}; + + let searchResultCount = 0; + $(window).on('action:ajaxify.start', function (ev, data) { + if (!String(data.url).startsWith('category/')) { + navigator.disable(); + } + }); + + Category.init = function () { + const cid = ajaxify.data.cid; + Category.cid = cid; + + app.enterRoom('category_' + cid); + + share.addShareHandlers(ajaxify.data.name); + + topicList.init('category', loadTopicsAfter); + + sort.handleSort('categoryTopicSort', 'category/' + ajaxify.data.slug); + if (!config.usePagination) { + navigator.init('[component="category/topic"]', ajaxify.data.topic_count, Category.toTop, Category.toBottom); + } else { + navigator.disable(); + } + + handleScrollToTopicIndex(); + + handleIgnoreWatch(cid); + + handleLoadMoreSubcategories(); + + handleBookmarks(); + + categorySelector.init($('[component="category-selector"]'), { + privilege: 'find', + parentCid: ajaxify.data.cid, + onSelect: function (category) { + ajaxify.go('/category/' + category.cid); + }, + }); + + hooks.fire('action:topics.loaded', { topics: ajaxify.data.topics }); + hooks.fire('action:category.loaded', { cid: ajaxify.data.cid }); + handleSearch(); + }; + + function handleScrollToTopicIndex() { + let topicIndex = ajaxify.data.topicIndex; + if (topicIndex && utils.isNumber(topicIndex)) { + topicIndex = Math.max(0, parseInt(topicIndex, 10)); + if (topicIndex && window.location.search.indexOf('page=') === -1) { + navigator.scrollToElement($('[component="category/topic"][data-index="' + topicIndex + '"]'), true, 0); + } + } + } + + function handleSearch(params) { + searchResultCount = params && params.resultCount; + $('#search-user').on('keyup', utils.debounce(doSearch, 250)); + } + + function doSearch() { + $('[component="user/search/icon"]').removeClass('fa-search').addClass('fa-spinner fa-spin'); + + const query = $('#search-user').val().trim(); + const cid = ajaxify.data.cid || Category.cid; + + const params = { + query: query, + cid: cid, + }; + + api.get('/api/v3/search/topics', params) + .then(function(response) { + var topics = response.topics; + console.log('API response topics:', topics); + renderSearchResults(topics); + }); + + } + + function renderSearchResults(topics) { + if (searchResultCount) { + topics = topics.slice(0, searchResultCount); + } + + const tplData = { + topics: topics, + showSelect: ajaxify.data.showSelect, + template: { + name: 'category', + }, + }; + tplData.template.category = true; + app.parseAndTranslate('category', 'topics', tplData, function (html) { + const topicListEl = $('[component="category"]'); + topicListEl.empty().append(html); + topicListEl.find('.timeago').timeago(); + $('[component="user/search/icon"]').addClass('fa-search').removeClass('fa-spinner fa-spin'); + }); + } + + function handleBookmarks() { + $('[component="category/bookmark"]').on('click', async function () { + const $this = $(this); + const topicId = $this.data('tid'); + const isBookmarked = $this.hasClass('bookmarked'); + + try { + if (isBookmarked) { + await api.delete('/topics/' + topicId + '/bookmark'); + $this.removeClass('bookmarked').attr('title', 'Bookmark this topic'); + } else { + await api.post('/topics/' + topicId + '/bookmark'); + $this.addClass('bookmarked').attr('title', 'Unbookmark this topic'); + } + + alerts.success(isBookmarked ? 'Topic unbookmarked' : 'Topic bookmarked'); + } catch (err) { + alerts.error('Bookmark action failed. Please try again.'); + } + }); + } + + function handleIgnoreWatch(cid) { + $('[component="category/watching"], [component="category/tracking"], [component="category/ignoring"], [component="category/notwatching"]').on('click', function () { + const $this = $(this); + const state = $this.attr('data-state'); + + api.put('/categories/' + cid + '/watch', { state }, (err) => { + if (err) { + return alerts.error(err); + } + + $('[component="category/watching/menu"]').toggleClass('hidden', state !== 'watching'); + $('[component="category/watching/check"]').toggleClass('fa-check', state === 'watching'); + + $('[component="category/tracking/menu"]').toggleClass('hidden', state !== 'tracking'); + $('[component="category/tracking/check"]').toggleClass('fa-check', state === 'tracking'); + + $('[component="category/notwatching/menu"]').toggleClass('hidden', state !== 'notwatching'); + $('[component="category/notwatching/check"]').toggleClass('fa-check', state === 'notwatching'); + + $('[component="category/ignoring/menu"]').toggleClass('hidden', state !== 'ignoring'); + $('[component="category/ignoring/check"]').toggleClass('fa-check', state === 'ignoring'); + + alerts.success('[[category:' + state + '.message]]'); + }); + }); + } + + function handleLoadMoreSubcategories() { + $('[component="category/load-more-subcategories"]').on('click', async function () { + const btn = $(this); + const { categories: data } = await api.get('/categories/' + ajaxify.data.cid + '/children?start=' + ajaxify.data.nextSubCategoryStart); + btn.toggleClass('hidden', !data.length || data.length < ajaxify.data.subCategoriesPerPage); + if (!data.length) { + return; + } + app.parseAndTranslate('category', 'children', { children: data }, function (html) { + html.find('.timeago').timeago(); + $('[component="category/subcategory/container"]').append(html); + ajaxify.data.nextSubCategoryStart += ajaxify.data.subCategoriesPerPage; + ajaxify.data.subCategoriesLeft -= data.length; + btn.toggleClass('hidden', ajaxify.data.subCategoriesLeft <= 0) + .translateText('[[category:x-more-categories, ' + ajaxify.data.subCategoriesLeft + ']]'); + }); + + return false; + }); + } + + Category.toTop = function () { + navigator.scrollTop(0); + }; + + Category.toBottom = async () => { + const { count } = await api.get('/categories/' + ajaxify.data.category.cid + '/count'); + navigator.scrollBottom(count - 1); + }; + + function loadTopicsAfter(after, direction, callback) { + callback = callback || function () {}; + + hooks.fire('action:topics.loading'); + const params = utils.params(); + infinitescroll.loadMore('/categories/' + ajaxify.data.cid + '/topics', { + after: after, + direction: direction, + query: params, + categoryTopicSort: params.sort || config.categoryTopicSort, + }, function (data, done) { + hooks.fire('action:topics.loaded', { topics: data.topics }); + callback(data, done); + }); + } + + return Category; +}); +`; + + +let stage = new Iroh.Stage(code); + +stage.addListener(Iroh.FUNCTION) +.on("enter", (e) => { + console.log("Function entered:", e.name); +}) +.on("leave", (e) => { + console.log("Function left:", e.name); +}); + +stage.addListener(Iroh.VAR) +.on("after", (e) => { + console.log("Variable declared:", e.name, "with value:", e.value); +}); + +stage.addListener(Iroh.LOOP) +.on("enter", (e) => { + console.log("Entering loop at indent level:", e.indent); +}) +.on("leave", (e) => { + console.log("Leaving loop at indent level:", e.indent); +}); +eval(stage.script); + From f977e6d3c6c6df37058ec757f113aada9b5424e9 Mon Sep 17 00:00:00 2001 From: Andrew Date: Thu, 24 Oct 2024 18:53:23 -0400 Subject: [PATCH 2/4] add iroh for dynamic analysis --- iroh.js | 45 ++++++++++++++++++++++----------------------- 1 file changed, 22 insertions(+), 23 deletions(-) diff --git a/iroh.js b/iroh.js index 657c30a1a9..5c204573c6 100644 --- a/iroh.js +++ b/iroh.js @@ -1,10 +1,9 @@ +'use strict'; const Iroh = require('iroh'); // Define the code to analyze const code = ` -'use strict'; - define('forum/category', [ 'forum/infinitescroll', 'share', @@ -91,10 +90,10 @@ define('forum/category', [ api.get('/api/v3/search/topics', params) .then(function(response) { - var topics = response.topics; - console.log('API response topics:', topics); - renderSearchResults(topics); - }); + var topics = response.topics; + console.log('API response topics:', topics); + renderSearchResults(topics); + }); } @@ -219,27 +218,27 @@ define('forum/category', [ `; -let stage = new Iroh.Stage(code); +const stage = new Iroh.Stage(code); stage.addListener(Iroh.FUNCTION) -.on("enter", (e) => { - console.log("Function entered:", e.name); -}) -.on("leave", (e) => { - console.log("Function left:", e.name); -}); + .on('enter', (e) => { + console.log('Function entered:', e.name); + }) + .on('leave', (e) => { + console.log('Function left:', e.name); + }); stage.addListener(Iroh.VAR) -.on("after", (e) => { - console.log("Variable declared:", e.name, "with value:", e.value); -}); + .on('after', (e) => { + console.log('Variable declared:', e.name, 'with value:', e.value); + }); stage.addListener(Iroh.LOOP) -.on("enter", (e) => { - console.log("Entering loop at indent level:", e.indent); -}) -.on("leave", (e) => { - console.log("Leaving loop at indent level:", e.indent); -}); -eval(stage.script); + .on('enter', (e) => { + console.log('Entering loop at indent level:', e.indent); + }) + .on('leave', (e) => { + console.log('Leaving loop at indent level:', e.indent); + }); + From 54cf172950db8cc2cd221be90c0be5156d51cca8 Mon Sep 17 00:00:00 2001 From: Andrew Date: Thu, 24 Oct 2024 23:16:17 -0400 Subject: [PATCH 3/4] add package with iroh --- package.json | 200 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 200 insertions(+) create mode 100644 package.json diff --git a/package.json b/package.json new file mode 100644 index 0000000000..635d5d6357 --- /dev/null +++ b/package.json @@ -0,0 +1,200 @@ +{ + "name": "nodebb", + "license": "GPL-3.0", + "description": "NodeBB Forum", + "version": "3.8.4", + "homepage": "https://www.nodebb.org", + "repository": { + "type": "git", + "url": "https://github.com/NodeBB/NodeBB/" + }, + "main": "app.js", + "scripts": { + "start": "node loader.js", + "lint": "eslint --cache ./nodebb .", + "test": "nyc --reporter=html --reporter=text-summary mocha", + "coverage": "nyc report --reporter=text-lcov > ./coverage/lcov.info", + "coveralls": "nyc report --reporter=text-lcov | coveralls && rm -r coverage" + }, + "nyc": { + "exclude": [ + "src/upgrades/*", + "test/*" + ] + }, + "lint-staged": { + "*.js": [ + "eslint --fix" + ] + }, + "dependencies": { + "@adactive/bootstrap-tagsinput": "0.8.2", + "@fontsource/inter": "5.0.18", + "@fontsource/poppins": "5.0.14", + "@fortawesome/fontawesome-free": "6.5.2", + "@isaacs/ttlcache": "1.4.1", + "@nodebb/spider-detector": "2.0.3", + "@popperjs/core": "2.11.8", + "@socket.io/redis-adapter": "8.3.0", + "ace-builds": "1.33.2", + "archiver": "7.0.1", + "async": "3.2.5", + "autoprefixer": "10.4.19", + "bcryptjs": "2.4.3", + "benchpressjs": "2.5.1", + "body-parser": "1.20.2", + "bootbox": "6.0.0", + "bootstrap": "5.3.3", + "bootswatch": "5.3.3", + "chalk": "4.1.2", + "chart.js": "4.4.2", + "cli-graph": "3.2.2", + "clipboard": "2.0.11", + "colors": "1.4.0", + "commander": "12.0.0", + "compare-versions": "6.1.0", + "compression": "1.7.4", + "connect-flash": "0.1.1", + "connect-mongo": "5.1.0", + "connect-multiparty": "2.2.0", + "connect-pg-simple": "9.0.1", + "connect-redis": "7.1.1", + "cookie-parser": "1.4.6", + "cron": "3.1.7", + "cropperjs": "1.6.2", + "csrf-sync": "4.0.3", + "daemon": "1.1.0", + "diff": "5.2.0", + "esbuild": "0.21.2", + "express": "4.19.2", + "express-session": "1.18.0", + "express-useragent": "1.0.15", + "fetch-cookie": "3.0.1", + "file-loader": "6.2.0", + "fs-extra": "11.2.0", + "graceful-fs": "4.2.11", + "helmet": "7.1.0", + "html-to-text": "9.0.5", + "imagesloaded": "5.0.0", + "ioredis": "5.4.1", + "ipaddr.js": "2.2.0", + "iroh": "^0.3.0", + "jquery": "3.7.1", + "jquery-deserialize": "2.0.0", + "jquery-form": "4.3.0", + "jquery-serializeobject": "1.0.0", + "jquery-ui": "1.13.3", + "jsesc": "3.0.2", + "json2csv": "5.0.7", + "jsonwebtoken": "9.0.2", + "lodash": "4.17.21", + "logrotate-stream": "0.2.9", + "lru-cache": "10.2.2", + "mime": "3.0.0", + "mkdirp": "3.0.1", + "mongodb": "6.6.1", + "morgan": "1.10.0", + "mousetrap": "1.6.5", + "multiparty": "4.2.3", + "nconf": "0.12.1", + "nodebb-plugin-2factor": "7.5.3", + "nodebb-plugin-composer-default": "10.2.36", + "nodebb-plugin-dbsearch": "6.2.5", + "nodebb-plugin-emoji": "5.1.15", + "nodebb-plugin-emoji-android": "4.0.0", + "nodebb-plugin-location-to-map": "^0.1.1", + "nodebb-plugin-markdown": "12.2.6", + "nodebb-plugin-mentions": "4.4.3", + "nodebb-plugin-ntfy": "1.7.4", + "nodebb-plugin-spam-be-gone": "2.2.2", + "nodebb-rewards-essentials": "1.0.0", + "nodebb-theme-harmony": "file:../nodebb-frontend-f24-team-penguins", + "nodebb-theme-lavender": "7.1.8", + "nodebb-theme-peace": "2.2.6", + "nodebb-theme-persona": "13.3.25", + "nodebb-widget-essentials": "7.0.18", + "nodemailer": "6.9.13", + "nprogress": "0.2.0", + "passport": "0.7.0", + "passport-http-bearer": "1.0.1", + "passport-local": "1.0.0", + "pg": "8.11.5", + "pg-cursor": "2.10.5", + "postcss": "8.4.38", + "postcss-clean": "1.2.0", + "progress-webpack-plugin": "1.0.16", + "prompt": "1.3.0", + "rimraf": "5.0.7", + "rss": "1.2.2", + "rtlcss": "4.1.1", + "sanitize-html": "2.13.0", + "sass": "1.77.1", + "semver": "7.6.2", + "serve-favicon": "2.5.0", + "sharp": "0.32.6", + "sitemap": "7.1.1", + "socket.io": "4.7.5", + "socket.io-client": "4.7.5", + "sortablejs": "1.15.2", + "spdx-license-list": "6.9.0", + "terser-webpack-plugin": "5.3.10", + "textcomplete": "0.18.2", + "textcomplete.contenteditable": "0.1.1", + "timeago": "1.6.7", + "tinycon": "0.6.8", + "toobusy-js": "0.5.1", + "tough-cookie": "4.1.4", + "validator": "13.12.0", + "webpack": "5.91.0", + "webpack-merge": "5.10.0", + "winston": "3.13.0", + "workerpool": "9.1.1", + "xml": "1.0.1", + "xregexp": "5.1.1", + "yargs": "17.7.2", + "zxcvbn": "4.4.2" + }, + "devDependencies": { + "@apidevtools/swagger-parser": "10.1.0", + "@commitlint/cli": "19.3.0", + "@commitlint/config-angular": "19.3.0", + "coveralls": "3.1.1", + "eslint": "8.57.0", + "eslint-config-nodebb": "0.2.1", + "eslint-plugin-import": "2.29.1", + "grunt": "^1.6.1", + "grunt-contrib-watch": "1.1.0", + "husky": "8.0.3", + "jsdom": "24.0.0", + "lint-staged": "15.2.2", + "mocha": "10.4.0", + "mocha-lcov-reporter": "1.3.0", + "mockdate": "3.0.5", + "nyc": "15.1.0", + "smtp-server": "3.13.4" + }, + "optionalDependencies": { + "sass-embedded": "1.77.1" + }, + "resolutions": { + "*/jquery": "3.7.1" + }, + "bugs": { + "url": "https://github.com/NodeBB/NodeBB/issues" + }, + "engines": { + "node": ">=18" + }, + "maintainers": [ + { + "name": "Julian Lam", + "email": "julian@nodebb.org", + "url": "https://github.com/julianlam" + }, + { + "name": "Barış Soner Uşaklı", + "email": "baris@nodebb.org", + "url": "https://github.com/barisusakli" + } + ] +} From 72e1bcacc7b9d372854c9346ec98d529d183bc8e Mon Sep 17 00:00:00 2001 From: Andrew Date: Thu, 24 Oct 2024 23:27:16 -0400 Subject: [PATCH 4/4] add npm install for iroh --- .github/workflows/test.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index c4e8f090bd..396f291d32 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -48,6 +48,10 @@ jobs: with: useLockFile: false + + - name: Iroh Install + run: npm install iroh + - name: Setup for Redis env: SETUP: >-