diff --git a/package.json b/package.json index adc88a305..94d78c4a1 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,7 @@ "prepare": "node ./scripts/prepare.js", "lint": "eslint \"src/**/*.{js,jsx,ts,tsx,json}\" \"test/**/*.{js,jsx,ts,tsx,json}\"", "lint:fix": "eslint --fix \"src/**/*.{js,jsx,ts,tsx,json}\" \"test/**/*.{js,jsx,ts,tsx,json}\"", - "format": "prettier --write src/**/*.{js,jsx,ts,tsx,css,md,json,scss} test/**/*.{js,jsx,ts,tsx,json} --config ./.prettierrc", + "format": "prettier --write src/**/*.{js,jsx,ts,tsx,css,md,json,scss} test/**/*.{js,jsx,ts,tsx,json} packages/git-proxy-cli/test/**/*.{js,jsx,ts,tsx,json} packages/git-proxy-cli/index.js --config ./.prettierrc", "gen-schema-doc": "node ./scripts/doc-schema.js", "cypress:run": "cypress run" }, diff --git a/packages/git-proxy-cli/test/testCli.proxy.config.json b/packages/git-proxy-cli/test/testCli.proxy.config.json index 48073ef57..95b3266ec 100644 --- a/packages/git-proxy-cli/test/testCli.proxy.config.json +++ b/packages/git-proxy-cli/test/testCli.proxy.config.json @@ -1,8 +1,7 @@ { "tempPassword": { "sendEmail": false, - "emailConfig": { - } + "emailConfig": {} }, "authorisedList": [ { @@ -22,9 +21,9 @@ { "type": "mongo", "connectionString": "mongodb://localhost:27017/gitproxy", - "options": { + "options": { "useUnifiedTopology": true - }, + }, "enabled": false } ], diff --git a/src/config/env.ts b/src/config/env.ts index 85b8475b5..9bb0bad55 100644 --- a/src/config/env.ts +++ b/src/config/env.ts @@ -9,12 +9,12 @@ const { GIT_PROXY_SERVER_PORT = 8000, GIT_PROXY_HTTPS_SERVER_PORT = 8443, GIT_PROXY_UI_HOST = 'http://localhost', - GIT_PROXY_UI_PORT = 8080 + GIT_PROXY_UI_PORT = 8080, } = process.env; export const serverConfig: ServerConfig = { GIT_PROXY_SERVER_PORT, GIT_PROXY_HTTPS_SERVER_PORT, GIT_PROXY_UI_HOST, - GIT_PROXY_UI_PORT + GIT_PROXY_UI_PORT, }; diff --git a/src/db/file/pushes.ts b/src/db/file/pushes.ts index 2a54314ea..2d8c41efb 100644 --- a/src/db/file/pushes.ts +++ b/src/db/file/pushes.ts @@ -6,10 +6,17 @@ import { toClass } from '../helper'; import * as repo from './repo'; import { PushQuery } from '../types'; +const COMPACTION_INTERVAL = 1000 * 60 * 60 * 24; // once per day + +// these don't get coverage in tests as they have already been run once before the test +/* istanbul ignore if */ if (!fs.existsSync('./.data')) fs.mkdirSync('./.data'); +/* istanbul ignore if */ if (!fs.existsSync('./.data/db')) fs.mkdirSync('./.data/db'); const db = new Datastore({ filename: './.data/db/pushes.db', autoload: true }); +db.ensureIndex({ fieldName: 'id', unique: true }); +db.setAutocompactionInterval(COMPACTION_INTERVAL); const defaultPushQuery: PushQuery = { error: false, @@ -22,6 +29,8 @@ export const getPushes = (query: PushQuery) => { if (!query) query = defaultPushQuery; return new Promise((resolve, reject) => { db.find(query, (err: Error, docs: Action[]) => { + // ignore for code coverage as neDB rarely returns errors even for an invalid query + /* istanbul ignore if */ if (err) { reject(err); } else { @@ -38,6 +47,8 @@ export const getPushes = (query: PushQuery) => { export const getPush = async (id: string) => { return new Promise((resolve, reject) => { db.findOne({ id: id }, (err, doc) => { + // ignore for code coverage as neDB rarely returns errors even for an invalid query + /* istanbul ignore if */ if (err) { reject(err); } else { @@ -54,6 +65,8 @@ export const getPush = async (id: string) => { export const deletePush = async (id: string) => { return new Promise((resolve, reject) => { db.remove({ id }, (err) => { + // ignore for code coverage as neDB rarely returns errors even for an invalid query + /* istanbul ignore if */ if (err) { reject(err); } else { @@ -67,6 +80,8 @@ export const writeAudit = async (action: Action) => { return new Promise((resolve, reject) => { const options = { multi: false, upsert: true }; db.update({ id: action.id }, action, options, (err) => { + // ignore for code coverage as neDB rarely returns errors even for an invalid query + /* istanbul ignore if */ if (err) { reject(err); } else { @@ -115,7 +130,7 @@ export const cancel = async (id: string) => { return { message: `cancel ${id}` }; }; -export const canUserCancelPush = async (id: string, user: any) => { +export const canUserCancelPush = async (id: string, user: string) => { return new Promise(async (resolve) => { const pushDetail = await getPush(id); if (!pushDetail) { @@ -134,14 +149,14 @@ export const canUserCancelPush = async (id: string, user: any) => { }); }; -export const canUserApproveRejectPush = async (id: string, user: any) => { +export const canUserApproveRejectPush = async (id: string, user: string) => { return new Promise(async (resolve) => { const action = await getPush(id); if (!action) { resolve(false); return; } - const repoName = action?.repoName.replace('.git', ''); + const repoName = action.repoName.replace('.git', ''); const isAllowed = await repo.canUserApproveRejectPushRepo(repoName, user); resolve(isAllowed); diff --git a/src/db/file/repo.ts b/src/db/file/repo.ts index 8686899f5..fd7218c15 100644 --- a/src/db/file/repo.ts +++ b/src/db/file/repo.ts @@ -1,15 +1,31 @@ import fs from 'fs'; -import Datastore from '@seald-io/nedb' +import Datastore from '@seald-io/nedb'; import { Repo } from '../types'; +const COMPACTION_INTERVAL = 1000 * 60 * 60 * 24; // once per day + +// these don't get coverage in tests as they have already been run once before the test +/* istanbul ignore if */ if (!fs.existsSync('./.data')) fs.mkdirSync('./.data'); +/* istanbul ignore if */ if (!fs.existsSync('./.data/db')) fs.mkdirSync('./.data/db'); const db = new Datastore({ filename: './.data/db/repos.db', autoload: true }); +db.ensureIndex({ fieldName: 'name', unique: false }); +db.setAutocompactionInterval(COMPACTION_INTERVAL); + +const isBlank = (str: string) => { + return !str || /^\s*$/.test(str); +}; export const getRepos = async (query: any = {}) => { + if (query?.name) { + query.name = query.name.toLowerCase(); + } return new Promise((resolve, reject) => { - db.find({}, (err: Error, docs: Repo[]) => { + db.find(query, (err: Error, docs: Repo[]) => { + // ignore for code coverage as neDB rarely returns errors even for an invalid query + /* istanbul ignore if */ if (err) { reject(err); } else { @@ -21,7 +37,9 @@ export const getRepos = async (query: any = {}) => { export const getRepo = async (name: string) => { return new Promise((resolve, reject) => { - db.findOne({ name }, (err: Error | null, doc: Repo) => { + db.findOne({ name: name.toLowerCase() }, (err: Error | null, doc: Repo) => { + // ignore for code coverage as neDB rarely returns errors even for an invalid query + /* istanbul ignore if */ if (err) { reject(err); } else { @@ -31,8 +49,19 @@ export const getRepo = async (name: string) => { }); }; - export const createRepo = async (repo: Repo) => { + if (isBlank(repo.project)) { + throw new Error('Project name cannot be empty'); + } + if (isBlank(repo.name)) { + throw new Error('Repository name cannot be empty'); + } else { + repo.name = repo.name.toLowerCase(); + } + if (isBlank(repo.url)) { + throw new Error('URL cannot be empty'); + } + repo.users = { canPush: [], canAuthorise: [], @@ -40,6 +69,8 @@ export const createRepo = async (repo: Repo) => { return new Promise((resolve, reject) => { db.insert(repo, (err, doc) => { + // ignore for code coverage as neDB rarely returns errors even for an invalid query + /* istanbul ignore if */ if (err) { reject(err); } else { @@ -50,6 +81,8 @@ export const createRepo = async (repo: Repo) => { }; export const addUserCanPush = async (name: string, user: string) => { + name = name.toLowerCase(); + user = user.toLowerCase(); return new Promise(async (resolve, reject) => { const repo = await getRepo(name); if (!repo) { @@ -65,6 +98,8 @@ export const addUserCanPush = async (name: string, user: string) => { const options = { multi: false, upsert: false }; db.update({ name: name }, repo, options, (err) => { + // ignore for code coverage as neDB rarely returns errors even for an invalid query + /* istanbul ignore if */ if (err) { reject(err); } else { @@ -75,6 +110,8 @@ export const addUserCanPush = async (name: string, user: string) => { }; export const addUserCanAuthorise = async (name: string, user: string) => { + name = name.toLowerCase(); + user = user.toLowerCase(); return new Promise(async (resolve, reject) => { const repo = await getRepo(name); if (!repo) { @@ -91,6 +128,8 @@ export const addUserCanAuthorise = async (name: string, user: string) => { const options = { multi: false, upsert: false }; db.update({ name: name }, repo, options, (err) => { + // ignore for code coverage as neDB rarely returns errors even for an invalid query + /* istanbul ignore if */ if (err) { reject(err); } else { @@ -101,6 +140,8 @@ export const addUserCanAuthorise = async (name: string, user: string) => { }; export const removeUserCanAuthorise = async (name: string, user: string) => { + name = name.toLowerCase(); + user = user.toLowerCase(); return new Promise(async (resolve, reject) => { const repo = await getRepo(name); if (!repo) { @@ -112,6 +153,8 @@ export const removeUserCanAuthorise = async (name: string, user: string) => { const options = { multi: false, upsert: false }; db.update({ name: name }, repo, options, (err) => { + // ignore for code coverage as neDB rarely returns errors even for an invalid query + /* istanbul ignore if */ if (err) { reject(err); } else { @@ -122,6 +165,8 @@ export const removeUserCanAuthorise = async (name: string, user: string) => { }; export const removeUserCanPush = async (name: string, user: string) => { + name = name.toLowerCase(); + user = user.toLowerCase(); return new Promise(async (resolve, reject) => { const repo = await getRepo(name); if (!repo) { @@ -133,6 +178,8 @@ export const removeUserCanPush = async (name: string, user: string) => { const options = { multi: false, upsert: false }; db.update({ name: name }, repo, options, (err) => { + // ignore for code coverage as neDB rarely returns errors even for an invalid query + /* istanbul ignore if */ if (err) { reject(err); } else { @@ -143,8 +190,11 @@ export const removeUserCanPush = async (name: string, user: string) => { }; export const deleteRepo = async (name: string) => { + name = name.toLowerCase(); return new Promise((resolve, reject) => { db.remove({ name: name }, (err) => { + // ignore for code coverage as neDB rarely returns errors even for an invalid query + /* istanbul ignore if */ if (err) { reject(err); } else { @@ -156,6 +206,7 @@ export const deleteRepo = async (name: string) => { export const isUserPushAllowed = async (name: string, user: string) => { name = name.toLowerCase(); + user = user.toLowerCase(); return new Promise(async (resolve) => { const repo = await getRepo(name); if (!repo) { @@ -176,6 +227,7 @@ export const isUserPushAllowed = async (name: string, user: string) => { export const canUserApproveRejectPushRepo = async (name: string, user: string) => { name = name.toLowerCase(); + user = user.toLowerCase(); console.log(`checking if user ${user} can approve/reject for ${name}`); return new Promise(async (resolve) => { const repo = await getRepo(name); diff --git a/src/db/file/users.ts b/src/db/file/users.ts index d72443c97..263c612f4 100644 --- a/src/db/file/users.ts +++ b/src/db/file/users.ts @@ -2,14 +2,26 @@ import fs from 'fs'; import Datastore from '@seald-io/nedb'; import { User } from '../types'; +const COMPACTION_INTERVAL = 1000 * 60 * 60 * 24; // once per day + +// these don't get coverage in tests as they have already been run once before the test +/* istanbul ignore if */ if (!fs.existsSync('./.data')) fs.mkdirSync('./.data'); +/* istanbul ignore if */ if (!fs.existsSync('./.data/db')) fs.mkdirSync('./.data/db'); const db = new Datastore({ filename: './.data/db/users.db', autoload: true }); +// Using a unique constraint with the index +db.ensureIndex({ fieldName: 'username', unique: true }); +db.ensureIndex({ fieldName: 'email', unique: true }); +db.setAutocompactionInterval(COMPACTION_INTERVAL); + export const findUser = (username: string) => { return new Promise((resolve, reject) => { - db.findOne({ username: username }, (err: Error | null, doc: User) => { + db.findOne({ username: username.toLowerCase() }, (err: Error | null, doc: User) => { + // ignore for code coverage as neDB rarely returns errors even for an invalid query + /* istanbul ignore if */ if (err) { reject(err); } else { @@ -26,6 +38,8 @@ export const findUser = (username: string) => { export const findUserByOIDC = function (oidcId: string) { return new Promise((resolve, reject) => { db.findOne({ oidcId: oidcId }, (err, doc) => { + // ignore for code coverage as neDB rarely returns errors even for an invalid query + /* istanbul ignore if */ if (err) { reject(err); } else { @@ -40,8 +54,12 @@ export const findUserByOIDC = function (oidcId: string) { }; export const createUser = function (user: User) { + user.username = user.username.toLowerCase(); + user.email = user.email.toLowerCase(); return new Promise((resolve, reject) => { db.insert(user, (err) => { + // ignore for code coverage as neDB rarely returns errors even for an invalid query + /* istanbul ignore if */ if (err) { reject(err); } else { @@ -53,7 +71,9 @@ export const createUser = function (user: User) { export const deleteUser = (username: string) => { return new Promise((resolve, reject) => { - db.remove({ username: username }, (err) => { + db.remove({ username: username.toLowerCase() }, (err) => { + // ignore for code coverage as neDB rarely returns errors even for an invalid query + /* istanbul ignore if */ if (err) { reject(err); } else { @@ -64,21 +84,54 @@ export const deleteUser = (username: string) => { }; export const updateUser = (user: User) => { + user.username = user.username.toLowerCase(); + if (user.email) { + user.email = user.email.toLowerCase(); + } return new Promise((resolve, reject) => { - const options = { multi: false, upsert: false }; - db.update({ username: user.username }, user, options, (err) => { + // The mongo db adaptor adds fields to existing documents, where this adaptor replaces the document + // hence, retrieve and merge documents to avoid dropping fields (such as the gitaccount) + let existingUser; + db.findOne({ username: user.username }, (err, doc) => { + // ignore for code coverage as neDB rarely returns errors even for an invalid query + /* istanbul ignore if */ if (err) { reject(err); } else { - resolve(null); + if (!doc) { + existingUser = {}; + } else { + existingUser = doc; + } + + Object.assign(existingUser, user); + + const options = { multi: false, upsert: true }; + db.update({ username: user.username }, existingUser, options, (err) => { + // ignore for code coverage as neDB rarely returns errors even for an invalid query + /* istanbul ignore if */ + if (err) { + reject(err); + } else { + resolve(null); + } + }); } }); }); }; export const getUsers = (query: any = {}) => { + if (query.username) { + query.username = query.username.toLowerCase(); + } + if (query.email) { + query.email = query.email.toLowerCase(); + } return new Promise((resolve, reject) => { db.find(query, (err: Error, docs: User[]) => { + // ignore for code coverage as neDB rarely returns errors even for an invalid query + /* istanbul ignore if */ if (err) { reject(err); } else { diff --git a/src/db/mongo/helper.ts b/src/db/mongo/helper.ts index 335434ebb..c4956de0f 100644 --- a/src/db/mongo/helper.ts +++ b/src/db/mongo/helper.ts @@ -25,7 +25,7 @@ export const connect = async (collectionName: string): Promise => { export const findDocuments = async ( collectionName: string, filter: Filter = {}, - options: FindOptions = {} + options: FindOptions = {}, ): Promise => { const collection = await connect(collectionName); return collection.find(filter, options).toArray() as Promise; @@ -34,7 +34,7 @@ export const findDocuments = async ( export const findOneDocument = async ( collectionName: string, filter: Filter = {}, - options: FindOptions = {} + options: FindOptions = {}, ): Promise => { const collection = await connect(collectionName); return (await collection.findOne(filter, options)) as T | null; diff --git a/src/db/mongo/repo.ts b/src/db/mongo/repo.ts index 5e55d3d71..f299f907a 100644 --- a/src/db/mongo/repo.ts +++ b/src/db/mongo/repo.ts @@ -1,4 +1,4 @@ -import { Repo } from "../types"; +import { Repo } from '../types'; const connect = require('./helper').connect; const collectionName = 'repos'; @@ -13,11 +13,13 @@ export const getRepos = async (query: any = {}) => { }; export const getRepo = async (name: string) => { + name = name.toLowerCase(); const collection = await connect(collectionName); return collection.findOne({ name: { $eq: name } }); }; export const createRepo = async (repo: Repo) => { + repo.name = repo.name.toLowerCase(); console.log(`creating new repo ${JSON.stringify(repo)}`); if (isBlank(repo.project)) { @@ -42,35 +44,41 @@ export const createRepo = async (repo: Repo) => { export const addUserCanPush = async (name: string, user: string) => { name = name.toLowerCase(); + user = user.toLowerCase(); const collection = await connect(collectionName); await collection.updateOne({ name: name }, { $push: { 'users.canPush': user } }); }; export const addUserCanAuthorise = async (name: string, user: string) => { name = name.toLowerCase(); + user = user.toLowerCase(); const collection = await connect(collectionName); await collection.updateOne({ name: name }, { $push: { 'users.canAuthorise': user } }); }; export const removeUserCanPush = async (name: string, user: string) => { name = name.toLowerCase(); + user = user.toLowerCase(); const collection = await connect(collectionName); await collection.updateOne({ name: name }, { $pull: { 'users.canPush': user } }); }; export const removeUserCanAuthorise = async (name: string, user: string) => { name = name.toLowerCase(); + user = user.toLowerCase(); const collection = await connect(collectionName); await collection.updateOne({ name: name }, { $pull: { 'users.canAuthorise': user } }); }; export const deleteRepo = async (name: string) => { + name = name.toLowerCase(); const collection = await connect(collectionName); await collection.deleteMany({ name: name }); }; export const isUserPushAllowed = async (name: string, user: string) => { name = name.toLowerCase(); + user = user.toLowerCase(); return new Promise(async (resolve) => { const repo = await exports.getRepo(name); console.log(repo.users.canPush); @@ -86,6 +94,7 @@ export const isUserPushAllowed = async (name: string, user: string) => { export const canUserApproveRejectPushRepo = async (name: string, user: string) => { name = name.toLowerCase(); + user = user.toLowerCase(); console.log(`checking if user ${user} can approve/reject for ${name}`); return new Promise(async (resolve) => { const repo = await exports.getRepo(name); diff --git a/src/db/mongo/users.ts b/src/db/mongo/users.ts index 0bfa1a941..5bacb245d 100644 --- a/src/db/mongo/users.ts +++ b/src/db/mongo/users.ts @@ -1,14 +1,20 @@ -import { User } from "../types"; +import { User } from '../types'; const connect = require('./helper').connect; const collectionName = 'users'; export const findUser = async function (username: string) { const collection = await connect(collectionName); - return collection.findOne({ username: { $eq: username } }); + return collection.findOne({ username: { $eq: username.toLowerCase() } }); }; export const getUsers = async function (query: any = {}) { + if (query.username) { + query.username = query.username.toLowerCase(); + } + if (query.email) { + query.email = query.email.toLowerCase(); + } console.log(`Getting users for query= ${JSON.stringify(query)}`); const collection = await connect(collectionName); return collection.find(query, { password: 0 }).toArray(); @@ -16,17 +22,21 @@ export const getUsers = async function (query: any = {}) { export const deleteUser = async function (username: string) { const collection = await connect(collectionName); - return collection.deleteOne({ username: username }); + return collection.deleteOne({ username: username.toLowerCase() }); }; export const createUser = async function (user: User) { user.username = user.username.toLowerCase(); + user.email = user.email.toLowerCase(); const collection = await connect(collectionName); return collection.insertOne(user); }; export const updateUser = async (user: User) => { user.username = user.username.toLowerCase(); + if (user.email) { + user.email = user.email.toLowerCase(); + } const options = { upsert: true }; const collection = await connect(collectionName); await collection.updateOne({ username: user.username }, { $set: user }, options); diff --git a/src/db/types.ts b/src/db/types.ts index dba9bdf3a..04951a699 100644 --- a/src/db/types.ts +++ b/src/db/types.ts @@ -1,8 +1,8 @@ export type PushQuery = { error: boolean; - blocked: boolean, - allowPush: boolean, - authorised: boolean + blocked: boolean; + allowPush: boolean; + authorised: boolean; }; export type UserRole = 'canPush' | 'canAuthorise'; diff --git a/src/plugin.ts b/src/plugin.ts index f2bd8f26a..92fb9a99c 100644 --- a/src/plugin.ts +++ b/src/plugin.ts @@ -15,9 +15,11 @@ function isCompatiblePlugin(obj: any, propertyName: string = 'isGitProxyPlugin') // valid plugin objects will have the appropriate property set to true // if the prototype chain is exhausted, return false while (obj != null) { - if (Object.prototype.hasOwnProperty.call(obj, propertyName) && + if ( + Object.prototype.hasOwnProperty.call(obj, propertyName) && obj.isGitProxyPlugin && - Object.keys(obj).includes('exec')) { + Object.keys(obj).includes('exec') + ) { return true; } obj = Object.getPrototypeOf(obj); @@ -55,25 +57,28 @@ class PluginLoader { */ async load(): Promise { try { - const modulePromises = this.targets.map(target => - this._loadPluginModule(target).catch(error => { + const modulePromises = this.targets.map((target) => + this._loadPluginModule(target).catch((error) => { console.error(`Failed to load plugin: ${error}`); // TODO: log.error() return Promise.reject(error); // Or return an error object to handle it later - }) + }), ); const moduleResults = await Promise.allSettled(modulePromises); const loadedModules = moduleResults - .filter((result): result is PromiseFulfilledResult => result.status === 'fulfilled' && result.value !== null) - .map(result => result.value); + .filter( + (result): result is PromiseFulfilledResult => + result.status === 'fulfilled' && result.value !== null, + ) + .map((result) => result.value); console.log(`Found ${loadedModules.length} plugin modules`); // TODO: log.debug() - const pluginTypeResultPromises = loadedModules.map(mod => - this._getPluginObjects(mod).catch(error => { + const pluginTypeResultPromises = loadedModules.map((mod) => + this._getPluginObjects(mod).catch((error) => { console.error(`Failed to cast plugin objects: ${error}`); // TODO: log.error() return Promise.reject(error); // Or return an error object to handle it later - }) + }), ); const settledPluginTypeResults = await Promise.allSettled(pluginTypeResultPromises); @@ -81,16 +86,19 @@ class PluginLoader { * @type {PluginTypeResult[]} List of resolved PluginTypeResult objects */ const pluginTypeResults = settledPluginTypeResults - .filter((result): result is PromiseFulfilledResult => result.status === 'fulfilled' && result.value !== null) - .map(result => result.value); + .filter( + (result): result is PromiseFulfilledResult => + result.status === 'fulfilled' && result.value !== null, + ) + .map((result) => result.value); for (const result of pluginTypeResults) { - this.pushPlugins.push(...result.pushAction) - this.pullPlugins.push(...result.pullAction) + this.pushPlugins.push(...result.pushAction); + this.pullPlugins.push(...result.pullAction); } const combinedPlugins = [...this.pushPlugins, ...this.pullPlugins]; - combinedPlugins.forEach(plugin => { + combinedPlugins.forEach((plugin) => { console.log(`Loaded plugin: ${plugin.constructor.name}`); }); } catch (error) { @@ -128,7 +136,9 @@ class PluginLoader { console.log('found pull plugin', potentialModule.constructor.name); plugins.pullAction.push(potentialModule); } else { - console.error(`Error: Object ${potentialModule.constructor.name} does not seem to be a compatible plugin type`); + console.error( + `Error: Object ${potentialModule.constructor.name} does not seem to be a compatible plugin type`, + ); } } @@ -136,7 +146,7 @@ class PluginLoader { // `module.exports = new ProxyPlugin()` in CJS or `exports default new ProxyPlugin()` in ESM // the "module" is a single object that could be a plugin if (isCompatiblePlugin(pluginModule)) { - handlePlugin(pluginModule) + handlePlugin(pluginModule); } else { // handle the typical case of a module which exports multiple objects // module.exports = { x, y } (CJS) or multiple `export ...` statements (ESM) @@ -173,11 +183,11 @@ class PushActionPlugin extends ProxyPlugin { * Wrapper class which contains at least one function executed as part of the action chain for git push operations. * The function must be called `exec` and take in two parameters: an Express Request (req) and the current Action * executed in the chain (action). This function should return a Promise that resolves to an Action. - * + * * Optionally, child classes which extend this can simply define the `exec` function as their own property. * This is the preferred implementation when a custom plugin (subclass) has its own state or additional methods * that are required. - * + * * @param {function} exec - A function that: * - Takes in an Express Request object as the first parameter (`req`). * - Takes in an Action object as the second parameter (`action`). @@ -201,11 +211,11 @@ class PullActionPlugin extends ProxyPlugin { * Wrapper class which contains at least one function executed as part of the action chain for git pull operations. * The function must be called `exec` and take in two parameters: an Express Request (req) and the current Action * executed in the chain (action). This function should return a Promise that resolves to an Action. - * + * * Optionally, child classes which extend this can simply define the `exec` function as their own property. * This is the preferred implementation when a custom plugin (subclass) has its own state or additional methods * that are required. - * + * * @param {function} exec - A function that: * - Takes in an Express Request object as the first parameter (`req`). * - Takes in an Action object as the second parameter (`action`). @@ -218,9 +228,4 @@ class PullActionPlugin extends ProxyPlugin { } } -export { - PluginLoader, - PushActionPlugin, - PullActionPlugin, - isCompatiblePlugin, -} +export { PluginLoader, PushActionPlugin, PullActionPlugin, isCompatiblePlugin }; diff --git a/src/proxy/actions/Action.ts b/src/proxy/actions/Action.ts index 78dbc2ef0..b15b7c24c 100644 --- a/src/proxy/actions/Action.ts +++ b/src/proxy/actions/Action.ts @@ -1,5 +1,5 @@ -import { getProxyUrl } from "../../config"; -import { Step } from "./Step"; +import { getProxyUrl } from '../../config'; +import { Step } from './Step'; /** * Represents a commit. @@ -62,15 +62,15 @@ class Action { this.type = type; this.method = method; this.timestamp = timestamp; - this.project = repo.split("/")[0]; - this.repoName = repo.split("/")[1]; + this.project = repo.split('/')[0]; + this.repoName = repo.split('/')[1]; this.url = `${getProxyUrl()}/${repo}`; this.repo = repo; } /** * Add a step to the action. - * @param {Step} step + * @param {Step} step */ addStep(step: Step): void { this.steps.push(step); diff --git a/src/proxy/actions/Step.ts b/src/proxy/actions/Step.ts index cdb07bf95..6eb114e9c 100644 --- a/src/proxy/actions/Step.ts +++ b/src/proxy/actions/Step.ts @@ -1,4 +1,4 @@ -import { v4 as uuidv4 } from "uuid"; +import { v4 as uuidv4 } from 'uuid'; /** Class representing a Push Step. */ class Step { @@ -17,7 +17,7 @@ class Step { errorMessage: string | null = null, blocked: boolean = false, blockedMessage: string | null = null, - content: any = null + content: any = null, ) { this.id = uuidv4(); this.stepName = stepName; @@ -35,12 +35,12 @@ class Step { } setContent(content: any): void { - this.log("setting content"); + this.log('setting content'); this.content = content; } setAsyncBlock(message: string): void { - this.log("setting blocked"); + this.log('setting blocked'); this.blocked = true; this.blockedMessage = message; } diff --git a/src/proxy/actions/autoActions.ts b/src/proxy/actions/autoActions.ts index 03ed0529a..450c97d80 100644 --- a/src/proxy/actions/autoActions.ts +++ b/src/proxy/actions/autoActions.ts @@ -33,7 +33,4 @@ const attemptAutoRejection = async (action: Action) => { } }; -export { - attemptAutoApproval, - attemptAutoRejection, -}; +export { attemptAutoApproval, attemptAutoRejection }; diff --git a/src/proxy/actions/index.ts b/src/proxy/actions/index.ts index 72aa2918a..13f35276c 100644 --- a/src/proxy/actions/index.ts +++ b/src/proxy/actions/index.ts @@ -1,7 +1,4 @@ import { Action } from './Action'; import { Step } from './Step'; -export { - Action, - Step -} +export { Action, Step }; diff --git a/src/proxy/processors/pre-processor/parseAction.ts b/src/proxy/processors/pre-processor/parseAction.ts index ed610d9d1..a9c332fdc 100644 --- a/src/proxy/processors/pre-processor/parseAction.ts +++ b/src/proxy/processors/pre-processor/parseAction.ts @@ -1,6 +1,10 @@ import { Action } from '../../actions'; -const exec = async (req: { originalUrl: string; method: string; headers: Record }) => { +const exec = async (req: { + originalUrl: string; + method: string; + headers: Record; +}) => { const id = Date.now(); const timestamp = id; const repoName = getRepoNameFromUrl(req.originalUrl); diff --git a/src/proxy/processors/push-action/checkAuthorEmails.ts b/src/proxy/processors/push-action/checkAuthorEmails.ts index a25a0e6c9..367514e16 100644 --- a/src/proxy/processors/push-action/checkAuthorEmails.ts +++ b/src/proxy/processors/push-action/checkAuthorEmails.ts @@ -27,14 +27,16 @@ const isEmailAllowed = (email: string): boolean => { } return true; -} +}; const exec = async (req: any, action: Action): Promise => { console.log({ req, action }); const step = new Step('checkAuthorEmails'); - const uniqueAuthorEmails = [...new Set(action.commitData?.map((commit: Commit) => commit.authorEmail))]; + const uniqueAuthorEmails = [ + ...new Set(action.commitData?.map((commit: Commit) => commit.authorEmail)), + ]; console.log({ uniqueAuthorEmails }); const illegalEmails = uniqueAuthorEmails.filter((email) => !isEmailAllowed(email)); diff --git a/src/proxy/processors/push-action/checkCommitMessages.ts b/src/proxy/processors/push-action/checkCommitMessages.ts index 7a95f6c12..01517539d 100644 --- a/src/proxy/processors/push-action/checkCommitMessages.ts +++ b/src/proxy/processors/push-action/checkCommitMessages.ts @@ -47,7 +47,7 @@ const isMessageAllowed = (commitMessage: string): boolean => { } return true; -} +}; // Execute if the repo is approved const exec = async (req: any, action: Action): Promise => { diff --git a/src/proxy/processors/push-action/checkUserPushPermission.ts b/src/proxy/processors/push-action/checkUserPushPermission.ts index c712693e5..5ed660b44 100644 --- a/src/proxy/processors/push-action/checkUserPushPermission.ts +++ b/src/proxy/processors/push-action/checkUserPushPermission.ts @@ -30,8 +30,8 @@ const exec = async (req: any, action: Action): Promise => { step.setError( `Rejecting push as user ${action.user} ` + - `is not allowed to push on repo ` + - `${action.repo}`, + `is not allowed to push on repo ` + + `${action.repo}`, ); action.addStep(step); return action; diff --git a/src/proxy/processors/push-action/preReceive.ts b/src/proxy/processors/push-action/preReceive.ts index 1a8b23d89..1c3ad36b9 100644 --- a/src/proxy/processors/push-action/preReceive.ts +++ b/src/proxy/processors/push-action/preReceive.ts @@ -10,7 +10,7 @@ const sanitizeInput = (_req: any, action: Action): string => { const exec = async ( req: any, action: Action, - hookFilePath: string = './hooks/pre-receive.sh' + hookFilePath: string = './hooks/pre-receive.sh', ): Promise => { const step = new Step('executeExternalPreReceiveHook'); let stderrTrimmed = ''; diff --git a/src/proxy/processors/push-action/pullRemote.ts b/src/proxy/processors/push-action/pullRemote.ts index c7559643f..2f7c808a2 100644 --- a/src/proxy/processors/push-action/pullRemote.ts +++ b/src/proxy/processors/push-action/pullRemote.ts @@ -1,5 +1,5 @@ import { Action, Step } from '../../actions'; -import fs from 'fs' +import fs from 'fs'; import git from 'isomorphic-git'; import gitHttpClient from 'isomorphic-git/http/node'; @@ -29,17 +29,16 @@ const exec = async (req: any, action: Action): Promise => { .toString() .split(':'); - await git - .clone({ - fs, - http: gitHttpClient, - url: action.url, - onAuth: () => ({ - username, - password, - }), - dir: `${action.proxyGitPath}/${action.repoName}`, - }); + await git.clone({ + fs, + http: gitHttpClient, + url: action.url, + onAuth: () => ({ + username, + password, + }), + dir: `${action.proxyGitPath}/${action.repoName}`, + }); console.log('Clone Success: ', action.url); diff --git a/src/proxy/processors/push-action/scanDiff.ts b/src/proxy/processors/push-action/scanDiff.ts index 296c8b404..40b39627b 100644 --- a/src/proxy/processors/push-action/scanDiff.ts +++ b/src/proxy/processors/push-action/scanDiff.ts @@ -8,13 +8,13 @@ const privateOrganizations = getPrivateOrganizations(); const BLOCK_TYPE = { LITERAL: 'Offending Literal', PATTERN: 'Offending Pattern', - PROVIDER: 'PROVIDER' -} + PROVIDER: 'PROVIDER', +}; type CombinedMatch = { type: string; match: RegExp; -} +}; type RawMatch = { type: string; @@ -22,7 +22,7 @@ type RawMatch = { file?: string; lines: number[]; content: string; -} +}; type Match = { type: string; @@ -30,7 +30,7 @@ type Match = { file?: string; lines: string; content: string; -} +}; const getDiffViolations = (diff: string, organization: string): Match[] | string | null => { // Commit diff is empty, i.e. '', null or undefined @@ -53,7 +53,7 @@ const getDiffViolations = (diff: string, organization: string): Match[] | string if (res.length > 0) { console.log('Diff is blocked via configured literals/patterns/providers...'); // combining matches with file and line number - return res + return res; } return null; @@ -67,66 +67,64 @@ const combineMatches = (organization: string) => { const blockedPatterns: string[] = commitConfig.diff.block.patterns; // Configured blocked providers - const blockedProviders: [string, string][] = organization && privateOrganizations.includes(organization) ? [] : - Object.entries(commitConfig.diff.block.providers); + const blockedProviders: [string, string][] = + organization && privateOrganizations.includes(organization) + ? [] + : Object.entries(commitConfig.diff.block.providers); // Combine all matches (literals, paterns) const combinedMatches = [ - ...blockedLiterals.map(literal => ({ + ...blockedLiterals.map((literal) => ({ type: BLOCK_TYPE.LITERAL, - match: new RegExp(literal, 'gi') + match: new RegExp(literal, 'gi'), })), - ...blockedPatterns.map(pattern => ({ + ...blockedPatterns.map((pattern) => ({ type: BLOCK_TYPE.PATTERN, - match: new RegExp(pattern, 'gi') + match: new RegExp(pattern, 'gi'), })), ...blockedProviders.map(([key, value]) => ({ type: key, - match: new RegExp(value, 'gi') + match: new RegExp(value, 'gi'), })), ]; return combinedMatches; -} +}; -const collectMatches = ( - parsedDiff: File[], - combinedMatches: CombinedMatch[] -): Match[] => { +const collectMatches = (parsedDiff: File[], combinedMatches: CombinedMatch[]): Match[] => { const allMatches: Record = {}; - parsedDiff.forEach(file => { + parsedDiff.forEach((file) => { const fileName = file.to || file.from; - console.log("CHANGE", file.chunks) + console.log('CHANGE', file.chunks); - file.chunks.forEach(chunk => { - chunk.changes.forEach(change => { - console.log("CHANGE", change) + file.chunks.forEach((chunk) => { + chunk.changes.forEach((change) => { + console.log('CHANGE', change); if (change.type === 'add') { // store line number const lineNumber = change.ln; // Iterate through each match types - literal, patterns, providers combinedMatches.forEach(({ type, match }) => { // using Match all to find all occurences of the pattern in the line - const matches = [...change.content.matchAll(match)] + const matches = [...change.content.matchAll(match)]; - matches.forEach(matchInstance => { + matches.forEach((matchInstance) => { const matchLiteral = matchInstance[0]; const matchKey = `${type}_${matchLiteral}_${fileName}`; // unique key - if (!allMatches[matchKey]) { - // match entry + // match entry allMatches[matchKey] = { type, literal: matchLiteral, file: fileName, lines: [], - content: change.content.trim() + content: change.content.trim(), }; } // apend line numbers to the list of lines - allMatches[matchKey].lines.push(lineNumber) - }) + allMatches[matchKey].lines.push(lineNumber); + }); }); } }); @@ -134,13 +132,13 @@ const collectMatches = ( }); // convert matches into a final result array, joining line numbers - const result = Object.values(allMatches).map(match => ({ + const result = Object.values(allMatches).map((match) => ({ ...match, - lines: match.lines.join(',') // join the line numbers into a comma-separated string - })) + lines: match.lines.join(','), // join the line numbers into a comma-separated string + })); return result; -} +}; const formatMatches = (matches: Match[]) => { return matches.map((match, index) => { @@ -148,9 +146,9 @@ const formatMatches = (matches: Match[]) => { Policy Exception Type: ${match.type} DETECTED: ${match.literal} FILE(S) LOCATED: ${match.file} - Line(s) of code: ${match.lines}` + Line(s) of code: ${match.lines}`; }); -} +}; const exec = async (req: any, action: Action): Promise => { const step = new Step('scanDiff'); @@ -160,24 +158,24 @@ const exec = async (req: any, action: Action): Promise => { const diff = steps.find((s) => s.stepName === 'diff')?.content; - console.log(diff) + console.log(diff); const diffViolations = getDiffViolations(diff, action.project); if (diffViolations) { - const formattedMatches = Array.isArray(diffViolations) ? formatMatches(diffViolations).join('\n\n') : diffViolations; + const formattedMatches = Array.isArray(diffViolations) + ? formatMatches(diffViolations).join('\n\n') + : diffViolations; const errorMsg = []; errorMsg.push(`\n\n\n\nYour push has been blocked.\n`); errorMsg.push(`Please ensure your code does not contain sensitive information or URLs.\n\n`); - errorMsg.push(formattedMatches) - errorMsg.push('\n') + errorMsg.push(formattedMatches); + errorMsg.push('\n'); console.log(`The following diff is illegal: ${commitFrom}:${commitTo}`); step.error = true; step.log(`The following diff is illegal: ${commitFrom}:${commitTo}`); - step.setError( - errorMsg.join('\n') - ); + step.setError(errorMsg.join('\n')); action.addStep(step); return action; diff --git a/src/proxy/processors/types.ts b/src/proxy/processors/types.ts index bb267ce90..ae92ba48c 100644 --- a/src/proxy/processors/types.ts +++ b/src/proxy/processors/types.ts @@ -1,4 +1,4 @@ -import { Action } from "../actions"; +import { Action } from '../actions'; export interface Processor { exec(req: any, action: Action): Promise; @@ -17,4 +17,4 @@ export type CommitContent = { deflatedSize: number; objectRef: any; content: string; -} +}; diff --git a/src/proxy/routes/index.ts b/src/proxy/routes/index.ts index c49853376..973608169 100644 --- a/src/proxy/routes/index.ts +++ b/src/proxy/routes/index.ts @@ -132,9 +132,4 @@ const handleMessage = (message: string): string => { return packetMessage; }; -export { - router, - handleMessage, - validGitRequest, - stripGitHubFromGitPath, -}; +export { router, handleMessage, validGitRequest, stripGitHubFromGitPath }; diff --git a/src/service/passport/oidc.js b/src/service/passport/oidc.js index 18fdf7de9..52928460b 100644 --- a/src/service/passport/oidc.js +++ b/src/service/passport/oidc.js @@ -7,11 +7,13 @@ const configure = async (passport) => { const { discovery, fetchUserInfo } = await import('openid-client'); const { Strategy } = await import('openid-client/passport'); const authMethods = require('../../config').getAuthMethods(); - const oidcConfig = authMethods.find((method) => method.type.toLowerCase() === "openidconnect")?.oidcConfig; + const oidcConfig = authMethods.find( + (method) => method.type.toLowerCase() === 'openidconnect', + )?.oidcConfig; const { issuer, clientID, clientSecret, callbackURL, scope } = oidcConfig; if (!oidcConfig || !oidcConfig.issuer) { - throw new Error('Missing OIDC issuer in configuration') + throw new Error('Missing OIDC issuer in configuration'); } const server = new URL(issuer); @@ -26,7 +28,7 @@ const configure = async (passport) => { const userInfo = await fetchUserInfo(config, tokenSet.access_token, expectedSub); handleUserAuthentication(userInfo, done); }); - + // currentUrl must be overridden to match the callback URL strategy.currentUrl = function (request) { const callbackUrl = new URL(callbackURL); @@ -41,7 +43,7 @@ const configure = async (passport) => { passport.serializeUser((user, done) => { done(null, user.oidcId || user.username); - }) + }); passport.deserializeUser(async (id, done) => { try { @@ -50,18 +52,18 @@ const configure = async (passport) => { } catch (err) { done(err); } - }) + }); return passport; } catch (error) { console.error('OIDC configuration failed:', error); throw error; } -} +}; /** * Handles user authentication with OIDC. - * @param {Object} userInfo the OIDC user info object + * @param {Object} userInfo the OIDC user info object * @param {Function} done the callback function * @return {Promise} a promise with the authenticated user or an error */ @@ -97,7 +99,9 @@ const handleUserAuthentication = async (userInfo, done) => { * @return {string | null} the email address */ const safelyExtractEmail = (profile) => { - return profile.email || (profile.emails && profile.emails.length > 0 ? profile.emails[0].value : null); + return ( + profile.email || (profile.emails && profile.emails.length > 0 ? profile.emails[0].value : null) + ); }; /** diff --git a/src/service/routes/auth.js b/src/service/routes/auth.js index aaf2efa26..9544931d9 100644 --- a/src/service/routes/auth.js +++ b/src/service/routes/auth.js @@ -3,7 +3,8 @@ const router = new express.Router(); const passport = require('../passport').getPassport(); const authStrategies = require('../passport').authStrategies; const db = require('../../db'); -const { GIT_PROXY_UI_HOST: uiHost = 'http://localhost', GIT_PROXY_UI_PORT: uiPort = 3000 } = process.env; +const { GIT_PROXY_UI_HOST: uiHost = 'http://localhost', GIT_PROXY_UI_PORT: uiPort = 3000 } = + process.env; router.get('/', (req, res) => { res.status(200).json({ diff --git a/src/service/routes/repo.js b/src/service/routes/repo.js index 1f6698e3b..f43181a00 100644 --- a/src/service/routes/repo.js +++ b/src/service/routes/repo.js @@ -5,9 +5,7 @@ const { getProxyURL } = require('../urls'); router.get('/', async (req, res) => { const proxyURL = getProxyURL(req); - const query = { - type: 'push', - }; + const query = {}; for (const k in req.query) { if (!k) continue; diff --git a/src/ui/components/Filtering/Filtering.css b/src/ui/components/Filtering/Filtering.css index 84f9258e0..a83724cb2 100644 --- a/src/ui/components/Filtering/Filtering.css +++ b/src/ui/components/Filtering/Filtering.css @@ -52,4 +52,4 @@ .dropdown-item:hover { background-color: #f0f0f0; -} \ No newline at end of file +} diff --git a/src/ui/components/Filtering/Filtering.jsx b/src/ui/components/Filtering/Filtering.jsx index aa9d26c78..1c9e2fcc7 100644 --- a/src/ui/components/Filtering/Filtering.jsx +++ b/src/ui/components/Filtering/Filtering.jsx @@ -27,29 +27,34 @@ const Filtering = ({ onFilterChange }) => { }; return ( -
-
+
+
{/* Make the entire button clickable for toggling dropdown */} - {isOpen && ( -
-
handleOptionClick('Date Modified')} className="dropdown-item"> +
+
handleOptionClick('Date Modified')} className='dropdown-item'> Date Modified
-
handleOptionClick('Date Created')} className="dropdown-item"> +
handleOptionClick('Date Created')} className='dropdown-item'> Date Created
-
handleOptionClick('Alphabetical')} className="dropdown-item"> +
handleOptionClick('Alphabetical')} className='dropdown-item'> Alphabetical
@@ -60,7 +65,3 @@ const Filtering = ({ onFilterChange }) => { }; export default Filtering; - - - - diff --git a/src/ui/components/Pagination/Pagination.jsx b/src/ui/components/Pagination/Pagination.jsx index e87e43c17..777807343 100644 --- a/src/ui/components/Pagination/Pagination.jsx +++ b/src/ui/components/Pagination/Pagination.jsx @@ -1,8 +1,7 @@ import React from 'react'; -import './Pagination.css'; +import './Pagination.css'; export default function Pagination({ currentPage, totalItems = 0, itemsPerPage, onPageChange }) { - const totalPages = Math.ceil(totalItems / itemsPerPage); const handlePageClick = (page) => { diff --git a/src/ui/components/Search/Search.css b/src/ui/components/Search/Search.css index db87dc8c0..d4c650d13 100644 --- a/src/ui/components/Search/Search.css +++ b/src/ui/components/Search/Search.css @@ -1,7 +1,7 @@ .search-bar { width: 100%; - max-width:100%; - margin: 0 auto 20px auto; + max-width: 100%; + margin: 0 auto 20px auto; } .search-input { @@ -10,9 +10,9 @@ font-size: 16px; border: 1px solid #ccc; border-radius: 4px; - box-sizing: border-box; + box-sizing: border-box; } .search-input:focus { - border-color: #007bff; + border-color: #007bff; } diff --git a/src/ui/components/Search/Search.jsx b/src/ui/components/Search/Search.jsx index 5e1cbf6b4..e774ea0f2 100644 --- a/src/ui/components/Search/Search.jsx +++ b/src/ui/components/Search/Search.jsx @@ -2,27 +2,26 @@ import React from 'react'; import { TextField } from '@material-ui/core'; import './Search.css'; import InputAdornment from '@material-ui/core/InputAdornment'; -import SearchIcon from '@material-ui/icons/Search'; - +import SearchIcon from '@material-ui/icons/Search'; export default function Search({ onSearch }) { const handleSearchChange = (event) => { const query = event.target.value; - onSearch(query); + onSearch(query); }; return (
+ ), @@ -31,7 +30,3 @@ export default function Search({ onSearch }) {
); } - - - - diff --git a/src/ui/views/RepoList/Components/Repositories.jsx b/src/ui/views/RepoList/Components/Repositories.jsx index ac9663423..228190903 100644 --- a/src/ui/views/RepoList/Components/Repositories.jsx +++ b/src/ui/views/RepoList/Components/Repositories.jsx @@ -14,8 +14,7 @@ import { UserContext } from '../../../../context'; import PropTypes from 'prop-types'; import Search from '../../../components/Search/Search'; import Pagination from '../../../components/Pagination/Pagination'; -import Filtering from '../../../components/Filtering/Filtering'; - +import Filtering from '../../../components/Filtering/Filtering'; export default function Repositories(props) { const useStyles = makeStyles(styles); @@ -26,7 +25,7 @@ export default function Repositories(props) { const [isLoading, setIsLoading] = useState(false); const [isError, setIsError] = useState(false); const [currentPage, setCurrentPage] = useState(1); - const itemsPerPage = 5; + const itemsPerPage = 5; const navigate = useNavigate(); const { user } = useContext(UserContext); const openRepo = (repo) => navigate(`/dashboard/repo/${repo}`, { replace: true }); @@ -37,10 +36,16 @@ export default function Repositories(props) { if (!k) continue; query[k] = props[k]; } - getRepos(setIsLoading, (data) => { - setData(data); - setFilteredData(data); - }, setAuth, setIsError, query); + getRepos( + setIsLoading, + (data) => { + setData(data); + setFilteredData(data); + }, + setAuth, + setIsError, + query, + ); }, [props]); const refresh = async (repo) => { @@ -50,16 +55,17 @@ export default function Repositories(props) { }; const handleSearch = (query) => { - setCurrentPage(1); + setCurrentPage(1); if (!query) { setFilteredData(data); } else { const lowercasedQuery = query.toLowerCase(); setFilteredData( - data.filter(repo => - repo.name.toLowerCase().includes(lowercasedQuery) || - repo.project.toLowerCase().includes(lowercasedQuery) - ) + data.filter( + (repo) => + repo.name.toLowerCase().includes(lowercasedQuery) || + repo.project.toLowerCase().includes(lowercasedQuery), + ), ); } }; @@ -88,8 +94,7 @@ export default function Repositories(props) { setFilteredData(sortedData); }; - - const handlePageChange = (page) => setCurrentPage(page); + const handlePageChange = (page) => setCurrentPage(page); const startIdx = (currentPage - 1) * itemsPerPage; const paginatedData = filteredData.slice(startIdx, startIdx + itemsPerPage); @@ -109,14 +114,14 @@ export default function Repositories(props) { key='x' classes={classes} openRepo={openRepo} - data={paginatedData} + data={paginatedData} repoButton={addrepoButton} - onSearch={handleSearch} - currentPage={currentPage} - totalItems={filteredData.length} - itemsPerPage={itemsPerPage} - onPageChange={handlePageChange} - onFilterChange={handleFilterChange} // Pass handleFilterChange as prop + onSearch={handleSearch} + currentPage={currentPage} + totalItems={filteredData.length} + itemsPerPage={itemsPerPage} + onPageChange={handlePageChange} + onFilterChange={handleFilterChange} // Pass handleFilterChange as prop /> ); } @@ -138,9 +143,8 @@ function GetGridContainerLayOut(props) { {props.repoButton} - - {/* Include the Filtering component */} + {/* Include the Filtering component */} @@ -166,4 +170,3 @@ function GetGridContainerLayOut(props) { ); } - diff --git a/src/ui/views/UserList/Components/UserList.jsx b/src/ui/views/UserList/Components/UserList.jsx index ee6812485..36aef89e6 100644 --- a/src/ui/views/UserList/Components/UserList.jsx +++ b/src/ui/views/UserList/Components/UserList.jsx @@ -20,7 +20,6 @@ import Search from '../../../components/Search/Search'; const useStyles = makeStyles(styles); export default function UserList(props) { - const classes = useStyles(); const [data, setData] = useState([]); const [, setAuth] = useState(true); @@ -28,12 +27,11 @@ export default function UserList(props) { const [isError, setIsError] = useState(false); const navigate = useNavigate(); const [currentPage, setCurrentPage] = useState(1); - const itemsPerPage = 5; + const itemsPerPage = 5; const [searchQuery, setSearchQuery] = useState(''); const openUser = (username) => navigate(`/dashboard/admin/user/${username}`, { replace: true }); - useEffect(() => { const query = {}; @@ -47,32 +45,30 @@ export default function UserList(props) { if (isLoading) return
Loading...
; if (isError) return
Something went wrong...
; - - const filteredUsers = data.filter(user => - user.displayName && user.displayName.toLowerCase().includes(searchQuery.toLowerCase()) || - user.username && user.username.toLowerCase().includes(searchQuery.toLowerCase()) -); + const filteredUsers = data.filter( + (user) => + (user.displayName && user.displayName.toLowerCase().includes(searchQuery.toLowerCase())) || + (user.username && user.username.toLowerCase().includes(searchQuery.toLowerCase())), + ); const indexOfLastItem = currentPage * itemsPerPage; const indexOfFirstItem = indexOfLastItem - itemsPerPage; const currentItems = filteredUsers.slice(indexOfFirstItem, indexOfLastItem); const totalItems = filteredUsers.length; - const handlePageChange = (page) => { setCurrentPage(page); }; - const handleSearch = (query) => { setSearchQuery(query); - setCurrentPage(1); + setCurrentPage(1); }; return ( - + @@ -94,12 +90,20 @@ export default function UserList(props) { {row.email} - + {row.gitAccount} - {row.admin ? : } + {row.admin ? ( + + ) : ( + + )}