From 3210beb5f1c411569c63a67d2904a44cbc3b0663 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Demory?= Date: Tue, 28 May 2024 16:54:02 +0200 Subject: [PATCH 1/6] refacto pagination + sanitize output --- admin/src/hooks/views/useViews.js | 24 +++++----- admin/src/index.js | 31 +++++++------ server/controllers/favorite-views.js | 27 +++++++++-- server/services/favorite-views.js | 69 +++++----------------------- 4 files changed, 64 insertions(+), 87 deletions(-) diff --git a/admin/src/hooks/views/useViews.js b/admin/src/hooks/views/useViews.js index 2976911..d88a2fd 100644 --- a/admin/src/hooks/views/useViews.js +++ b/admin/src/hooks/views/useViews.js @@ -46,8 +46,8 @@ const useViews = () => { useEffect(() => { const query = new URLSearchParams(location.search); - const page = Number(query.get('page')); - const pageSize = Number(query.get('pageSize')); + const page = Number(query.get('page')) === 0 ? 1 : Number(query.get('page')); + const pageSize = Number(query.get('pageSize')) === 0 ? 10 : Number(query.get('pageSize')); setFetchParams({ currentPage: page, @@ -71,12 +71,12 @@ const useViews = () => { const response = await get( `${CONST.REQUEST_URLS.GET_USER_VIEWS}?page=${page}&pageSize=${pageSize}&sortBy=createdAt:asc` ); - + console.log(response); const { userViewsData, pagination } = response.data; setViews(userViewsData || []); setViewsPagination({ - count: Number(pagination.count), - totalPages: Number(pagination.totalPages) + count: Number(pagination.total), + totalPages: Number(pagination.pageCount) }); }, [fetchParams] @@ -87,12 +87,12 @@ const useViews = () => { const response = await get( `${CONST.REQUEST_URLS.GET_SHARED_VIEWS}?page=${page}&pageSize=${pageSize}&sortBy=createdAt:asc` ); - + console.log(response); const { sharedViewsData, pagination } = response.data; setViews(sharedViewsData || []); setViewsPagination({ - count: Number(pagination.count), - totalPages: Number(pagination.totalPages) + count: Number(pagination.total), + totalPages: Number(pagination.pageCount) }); }, [fetchParams] @@ -131,10 +131,10 @@ const useViews = () => { await del(`${CONST.REQUEST_URLS.DELETE_VIEW}${id}`); if (tabsIndex === CONST.TABS_INDEX.userViewsTab) { - getUserViews(fetchParams.page, fetchParams.pageSize); + getUserViews(fetchParams.currentPage, fetchParams.viewsPerPage); } if (tabsIndex === CONST.TABS_INDEX.sharedViewsTab) { - getSharedViews(fetchParams.page, fetchParams.pageSize); + getSharedViews(fetchParams.currentPage, fetchParams.viewsPerPage); } toggleNotification({ @@ -154,10 +154,10 @@ const useViews = () => { await put(`${CONST.REQUEST_URLS.UPDATE_VIEW}${id}`, viewData); if (tabsIndex === CONST.TABS_INDEX.userViewsTab) { - getUserViews(fetchParams.page, fetchParams.pageSize); + getUserViews(fetchParams.currentPage, fetchParams.viewsPerPage); } if (tabsIndex === CONST.TABS_INDEX.sharedViewsTab) { - getSharedViews(fetchParams.page, fetchParams.pageSize); + getSharedViews(fetchParams.currentPage, fetchParams.viewsPerPage); } toggleNotification({ diff --git a/admin/src/index.js b/admin/src/index.js index 99f29ad..34f2cbf 100644 --- a/admin/src/index.js +++ b/admin/src/index.js @@ -52,23 +52,24 @@ export default { ) }); - if (process.env.STRAPI_ADMIN_FAVORITE_VIEWS_INJECT_TO) { - for (const entry of process.env.STRAPI_ADMIN_FAVORITE_VIEWS_INJECT_TO.split(',')) { - const [pluginName, container, block] = entry.split('.'); - const plugin = app.getPlugin(pluginName.replace(/^plugin::/, '')); - if (!plugin) continue; + // if (process.env.STRAPI_ADMIN_FAVORITE_VIEWS_INJECT_TO) { + // for (const entry of process.env.STRAPI_ADMIN_FAVORITE_VIEWS_INJECT_TO.split(',')) { + // const [pluginName, container, block] = entry.split('.'); + // const plugin = app.getPlugin(pluginName.replace(/^plugin::/, '')); - plugin.injectComponent(container, block, { - name: 'favorite-views-widget', - Component: () => ( - - - - ) - }); - } - } + // if (!plugin) continue; + + // plugin.injectComponent(container, block, { + // name: 'favorite-views-widget', + // Component: () => ( + // + // + // + // ) + // }); + // } + // } }, async registerTrads({ locales }) { const importedTrads = await Promise.all( diff --git a/server/controllers/favorite-views.js b/server/controllers/favorite-views.js index 07049a2..9ec0f77 100644 --- a/server/controllers/favorite-views.js +++ b/server/controllers/favorite-views.js @@ -1,29 +1,47 @@ 'use strict'; +const { sanitize } = require('@strapi/utils'); + module.exports = ({ strapi }) => ({ async getUserViews(ctx) { + const contentType = strapi.contentType('plugin::favorite-views.saved-view'); const user = ctx.state.user; const { page = 1, pageSize = 10 } = ctx.query; - - ctx.body = await strapi + const { results, pagination } = await strapi .plugin('favorite-views') .service('favoriteViews') .getUserViews({ page, pageSize }, user); + + const userViewsData = await sanitize.contentAPI.output(results, contentType, { + auth: user + }); + + return { userViewsData, pagination }; }, + async getSharedViews(ctx) { + const contentType = strapi.contentType('plugin::favorite-views.saved-view'); const user = ctx.state.user; const { page = 1, pageSize = 10 } = ctx.query; - ctx.body = await strapi + const { results, pagination } = await strapi .plugin('favorite-views') .service('favoriteViews') .getSharedViews({ page, pageSize }, user); + + const sharedViewsData = await sanitize.contentAPI.output(results, contentType, { + auth: user + }); + + return { sharedViewsData, pagination }; }, + async getPrivateViews(ctx) { const user = ctx.state.user; ctx.body = await strapi.plugin('favorite-views').service('favoriteViews').getPrivateViews(user); }, + async create(ctx) { const userId = ctx.state.user.id; const { name, slug, roles, visibility } = ctx.request.body; @@ -33,11 +51,13 @@ module.exports = ({ strapi }) => ({ .service('favoriteViews') .create(name, slug, roles, visibility, userId); }, + async delete(ctx) { const { id } = ctx.params; ctx.body = await strapi.plugin('favorite-views').service('favoriteViews').delete(id); }, + async update(ctx) { const { id } = ctx.params; const userId = ctx.state.user.id; @@ -48,6 +68,7 @@ module.exports = ({ strapi }) => ({ .service('favoriteViews') .update(id, name, roles, visibility, userId); }, + async getRoles(ctx) { ctx.body = await strapi.plugin('favorite-views').service('favoriteViews').getRoles(); } diff --git a/server/services/favorite-views.js b/server/services/favorite-views.js index a64b0b4..a006736 100644 --- a/server/services/favorite-views.js +++ b/server/services/favorite-views.js @@ -3,51 +3,30 @@ module.exports = ({ strapi }) => ({ async getUserViews(params, user) { const { page = 1, pageSize = 10 } = params; - const startIndex = (page - 1) * pageSize; try { - let userViewsData = []; - let userViewsCount; if (user) { - userViewsData = await strapi.entityService.findMany('plugin::favorite-views.saved-view', { - start: startIndex, - limit: pageSize, + return await strapi.entityService.findPage('plugin::favorite-views.saved-view', { + page, + pageSize, filters: { createdBy: { id: user.id } }, populate: ['createdBy'] }); - - userViewsCount = await strapi.entityService.count('plugin::favorite-views.saved-view', { - filters: { - $and: [{ createdBy: { id: user.id } }, { visibility: 'private' }] - } - }); } - - return { - userViewsData, - pagination: { - currentPage: page, - pageSize, - totalPages: Math.ceil(userViewsCount / pageSize), - count: userViewsCount - } - }; } catch (error) { throw new Error(`Find favorite user views error : ${error}`); } }, + async getSharedViews(params, user) { const { page = 1, pageSize = 10 } = params; - const startIndex = (page - 1) * pageSize; try { - let sharedViewsData = []; - let sharedViewsCount; if (user.roles.length) { const userRoles = user.roles.map((role) => role.code); - sharedViewsData = await strapi.entityService.findMany('plugin::favorite-views.saved-view', { - start: startIndex, - limit: pageSize, + return await strapi.entityService.findPage('plugin::favorite-views.saved-view', { + page, + pageSize, filters: { $and: [ { createdBy: { id: { $ne: user.id } } }, @@ -66,40 +45,12 @@ module.exports = ({ strapi }) => ({ }, populate: ['createdBy'] }); - - sharedViewsCount = await strapi.entityService.count('plugin::favorite-views.saved-view', { - filters: { - $and: [ - { createdBy: { id: { $ne: user.id } } }, - { - $or: [ - { - $and: [ - { visibility: 'roles' }, - { $or: userRoles.map((role) => ({ roles: { $contains: role } })) } - ] - }, - { visibility: 'public' } - ] - } - ] - } - }); } - - return { - sharedViewsData, - pagination: { - currentPage: page, - pageSize, - totalPages: Math.ceil(sharedViewsCount / pageSize), - count: sharedViewsCount - } - }; } catch (error) { throw new Error(`Find favorite shared views error : ${error}`); } }, + async getPrivateViews(user) { try { let privateViewsData = []; @@ -122,6 +73,7 @@ module.exports = ({ strapi }) => ({ throw new Error(`Find favorite private views error : ${error}`); } }, + async create(name, slug, roles, visibility, userId) { if (userId) { const ADMIN_URL = strapi.admin.config.url || '/admin'; @@ -144,6 +96,7 @@ module.exports = ({ strapi }) => ({ throw new Error('UserId is not defined'); } }, + async delete(id) { if (id) { try { @@ -155,6 +108,7 @@ module.exports = ({ strapi }) => ({ throw new Error('Id is not defined'); } }, + async update(id, name, roles, visibility, userId) { if (id) { try { @@ -173,6 +127,7 @@ module.exports = ({ strapi }) => ({ throw new Error('Id is not defined'); } }, + async getRoles() { return await strapi.db.query('admin::role').findMany(); } From 95bf3150deaf6d6f48cec29078b3be2704737bdd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Demory?= Date: Tue, 28 May 2024 17:10:53 +0200 Subject: [PATCH 2/6] refacto pagination + sanitize output --- admin/src/components/ViewsTable/index.js | 2 +- admin/src/hooks/views/useViews.js | 2 - server/controllers/favorite-views.js | 11 ++++- server/routes/admin.js | 61 ++++++++++++++++++++++++ server/routes/index.js | 61 ++---------------------- 5 files changed, 74 insertions(+), 63 deletions(-) create mode 100644 server/routes/admin.js diff --git a/admin/src/components/ViewsTable/index.js b/admin/src/components/ViewsTable/index.js index 81bf435..400fbbb 100644 --- a/admin/src/components/ViewsTable/index.js +++ b/admin/src/components/ViewsTable/index.js @@ -182,7 +182,7 @@ export const TableFooter = () => { {translate('HomePage.Table.Footer.Pagination.Select')} - + { const response = await get( `${CONST.REQUEST_URLS.GET_USER_VIEWS}?page=${page}&pageSize=${pageSize}&sortBy=createdAt:asc` ); - console.log(response); const { userViewsData, pagination } = response.data; setViews(userViewsData || []); setViewsPagination({ @@ -87,7 +86,6 @@ const useViews = () => { const response = await get( `${CONST.REQUEST_URLS.GET_SHARED_VIEWS}?page=${page}&pageSize=${pageSize}&sortBy=createdAt:asc` ); - console.log(response); const { sharedViewsData, pagination } = response.data; setViews(sharedViewsData || []); setViewsPagination({ diff --git a/server/controllers/favorite-views.js b/server/controllers/favorite-views.js index 9ec0f77..f4db5a5 100644 --- a/server/controllers/favorite-views.js +++ b/server/controllers/favorite-views.js @@ -6,7 +6,11 @@ module.exports = ({ strapi }) => ({ async getUserViews(ctx) { const contentType = strapi.contentType('plugin::favorite-views.saved-view'); const user = ctx.state.user; - const { page = 1, pageSize = 10 } = ctx.query; + const sanitizedQueryParams = await sanitize.contentAPI.query(ctx.query, contentType, { + auth: ctx.state.auth + }); + const { page = 1, pageSize = 10 } = sanitizedQueryParams; + const { results, pagination } = await strapi .plugin('favorite-views') .service('favoriteViews') @@ -22,7 +26,10 @@ module.exports = ({ strapi }) => ({ async getSharedViews(ctx) { const contentType = strapi.contentType('plugin::favorite-views.saved-view'); const user = ctx.state.user; - const { page = 1, pageSize = 10 } = ctx.query; + const sanitizedQueryParams = await sanitize.contentAPI.query(ctx.query, contentType, { + auth: ctx.state.auth + }); + const { page = 1, pageSize = 10 } = sanitizedQueryParams; const { results, pagination } = await strapi .plugin('favorite-views') diff --git a/server/routes/admin.js b/server/routes/admin.js new file mode 100644 index 0000000..7b758ec --- /dev/null +++ b/server/routes/admin.js @@ -0,0 +1,61 @@ +module.exports = { + type: 'admin', + routes: [ + { + method: 'GET', + path: '/getUserViews', + handler: 'favoriteViews.getUserViews', + config: { + policies: [] + } + }, + { + method: 'GET', + path: '/getSharedViews', + handler: 'favoriteViews.getSharedViews', + config: { + policies: [] + } + }, + { + method: 'GET', + path: '/getPrivateViews', + handler: 'favoriteViews.getPrivateViews', + config: { + policies: [] + } + }, + { + method: 'POST', + path: '/create', + handler: 'favoriteViews.create', + config: { + policies: [] + } + }, + { + method: 'DELETE', + path: '/delete/:id', + handler: 'favoriteViews.delete', + config: { + policies: [] + } + }, + { + method: 'PUT', + path: '/update/:id', + handler: 'favoriteViews.update', + config: { + policies: [] + } + }, + { + method: 'GET', + path: '/getRoles', + handler: 'favoriteViews.getRoles', + config: { + policies: [] + } + } + ] +}; diff --git a/server/routes/index.js b/server/routes/index.js index c72b631..d3b87ad 100644 --- a/server/routes/index.js +++ b/server/routes/index.js @@ -1,58 +1,3 @@ -module.exports = [ - { - method: 'GET', - path: '/getUserViews', - handler: 'favoriteViews.getUserViews', - config: { - policies: [] - } - }, - { - method: 'GET', - path: '/getSharedViews', - handler: 'favoriteViews.getSharedViews', - config: { - policies: [] - } - }, - { - method: 'GET', - path: '/getPrivateViews', - handler: 'favoriteViews.getPrivateViews', - config: { - policies: [] - } - }, - { - method: 'POST', - path: '/create', - handler: 'favoriteViews.create', - config: { - policies: [] - } - }, - { - method: 'DELETE', - path: '/delete/:id', - handler: 'favoriteViews.delete', - config: { - policies: [] - } - }, - { - method: 'PUT', - path: '/update/:id', - handler: 'favoriteViews.update', - config: { - policies: [] - } - }, - { - method: 'GET', - path: '/getRoles', - handler: 'favoriteViews.getRoles', - config: { - policies: [] - } - } -]; +module.exports = { + admin: require('./admin') +}; From ac728fc66bf549ed57a91c874b86da59c7434643 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Demory?= Date: Tue, 28 May 2024 17:12:41 +0200 Subject: [PATCH 3/6] uncomment injection zone --- admin/src/index.js | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/admin/src/index.js b/admin/src/index.js index 34f2cbf..660a9b9 100644 --- a/admin/src/index.js +++ b/admin/src/index.js @@ -53,23 +53,23 @@ export default { ) }); - // if (process.env.STRAPI_ADMIN_FAVORITE_VIEWS_INJECT_TO) { - // for (const entry of process.env.STRAPI_ADMIN_FAVORITE_VIEWS_INJECT_TO.split(',')) { - // const [pluginName, container, block] = entry.split('.'); - // const plugin = app.getPlugin(pluginName.replace(/^plugin::/, '')); + if (process.env.STRAPI_ADMIN_FAVORITE_VIEWS_INJECT_TO) { + for (const entry of process.env.STRAPI_ADMIN_FAVORITE_VIEWS_INJECT_TO.split(',')) { + const [pluginName, container, block] = entry.split('.'); + const plugin = app.getPlugin(pluginName.replace(/^plugin::/, '')); - // if (!plugin) continue; + if (!plugin) continue; - // plugin.injectComponent(container, block, { - // name: 'favorite-views-widget', - // Component: () => ( - // - // - // - // ) - // }); - // } - // } + plugin.injectComponent(container, block, { + name: 'favorite-views-widget', + Component: () => ( + + + + ) + }); + } + } }, async registerTrads({ locales }) { const importedTrads = await Promise.all( From 158a019cdeac35d261744f8a122c30328d80251c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Demory?= Date: Wed, 29 May 2024 08:31:08 +0200 Subject: [PATCH 4/6] sanitize input --- server/controllers/favorite-views.js | 57 +++++++++++++++++++++++----- server/services/favorite-views.js | 20 +++------- 2 files changed, 54 insertions(+), 23 deletions(-) diff --git a/server/controllers/favorite-views.js b/server/controllers/favorite-views.js index f4db5a5..63b0105 100644 --- a/server/controllers/favorite-views.js +++ b/server/controllers/favorite-views.js @@ -44,36 +44,75 @@ module.exports = ({ strapi }) => ({ }, async getPrivateViews(ctx) { + const contentType = strapi.contentType('plugin::favorite-views.saved-view'); const user = ctx.state.user; - ctx.body = await strapi.plugin('favorite-views').service('favoriteViews').getPrivateViews(user); + const results = await strapi + .plugin('favorite-views') + .service('favoriteViews') + .getPrivateViews(user); + + const privateViewsData = await sanitize.contentAPI.output(results, contentType, { + auth: user + }); + + return privateViewsData; }, async create(ctx) { + const contentType = strapi.contentType('plugin::favorite-views.saved-view'); + const user = ctx.state.user; const userId = ctx.state.user.id; - const { name, slug, roles, visibility } = ctx.request.body; + const sanitizedRequestBody = await sanitize.contentAPI.input(ctx.request.body, contentType, { + auth: ctx.state.auth + }); + const { name, slug, roles, visibility } = sanitizedRequestBody; - ctx.body = await strapi + const result = await strapi .plugin('favorite-views') .service('favoriteViews') .create(name, slug, roles, visibility, userId); + + return await sanitize.contentAPI.output(result, contentType, { + auth: user + }); }, async delete(ctx) { - const { id } = ctx.params; + const contentType = strapi.contentType('plugin::favorite-views.saved-view'); + const user = ctx.state.user; + const sanitizedQueryParams = await sanitize.contentAPI.query(ctx.params, contentType, { + auth: ctx.state.auth + }); + const { id } = sanitizedQueryParams; + const result = await strapi.plugin('favorite-views').service('favoriteViews').delete(id); - ctx.body = await strapi.plugin('favorite-views').service('favoriteViews').delete(id); + return await sanitize.contentAPI.output(result, contentType, { + auth: user + }); }, async update(ctx) { - const { id } = ctx.params; - const userId = ctx.state.user.id; - const { name, roles, visibility } = ctx.request.body; + const contentType = strapi.contentType('plugin::favorite-views.saved-view'); + const user = ctx.state.user; + const userId = user.id; + const sanitizedQueryParams = await sanitize.contentAPI.query(ctx.params, contentType, { + auth: ctx.state.auth + }); + const { id } = sanitizedQueryParams; + const sanitizedRequestBody = await sanitize.contentAPI.input(ctx.request.body, contentType, { + auth: ctx.state.auth + }); + const { name, roles, visibility } = sanitizedRequestBody; - ctx.body = await strapi + const result = await strapi .plugin('favorite-views') .service('favoriteViews') .update(id, name, roles, visibility, userId); + + return await sanitize.contentAPI.output(result, contentType, { + auth: user + }); }, async getRoles(ctx) { diff --git a/server/services/favorite-views.js b/server/services/favorite-views.js index a006736..f174cb7 100644 --- a/server/services/favorite-views.js +++ b/server/services/favorite-views.js @@ -53,22 +53,14 @@ module.exports = ({ strapi }) => ({ async getPrivateViews(user) { try { - let privateViewsData = []; if (user) { - privateViewsData = await strapi.entityService.findMany( - 'plugin::favorite-views.saved-view', - { - filters: { - $and: [{ createdBy: { id: user.id } }, { visibility: 'private' }] - }, - populate: ['createdBy'] - } - ); + return await strapi.entityService.findMany('plugin::favorite-views.saved-view', { + filters: { + $and: [{ createdBy: { id: user.id } }, { visibility: 'private' }] + }, + populate: ['createdBy'] + }); } - - return { - privateViewsData - }; } catch (error) { throw new Error(`Find favorite private views error : ${error}`); } From 0832bd53c45f3836ca6666041797c0be5da8bd6e Mon Sep 17 00:00:00 2001 From: Olivier Malige Date: Mon, 10 Jun 2024 22:42:22 +0200 Subject: [PATCH 5/6] fix table pagination margin --- admin/src/components/ViewsTable/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/admin/src/components/ViewsTable/index.js b/admin/src/components/ViewsTable/index.js index 400fbbb..6fc3324 100644 --- a/admin/src/components/ViewsTable/index.js +++ b/admin/src/components/ViewsTable/index.js @@ -169,7 +169,7 @@ export const TableFooter = () => { }; return ( - + {ITEMS_PER_PAGE.map((option) => ( From 1c5e9e074c440a230624fea611768ba03f101cad Mon Sep 17 00:00:00 2001 From: Olivier Malige Date: Mon, 10 Jun 2024 23:06:33 +0200 Subject: [PATCH 6/6] Fix private views results --- admin/src/hooks/views/useViews.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/admin/src/hooks/views/useViews.js b/admin/src/hooks/views/useViews.js index 163d3c3..1680466 100644 --- a/admin/src/hooks/views/useViews.js +++ b/admin/src/hooks/views/useViews.js @@ -101,7 +101,7 @@ const useViews = () => { const getPrivateViews = async () => { const { data } = await get(CONST.REQUEST_URLS.GET_PRIVATE_VIEWS); - setPrivateViews(data.privateViewsData); + setPrivateViews(data); }; const addView = async (viewData) => {