diff --git a/client/admin/admin.jsx b/client/admin/admin.jsx index f2f2667a4..c24f7f176 100644 --- a/client/admin/admin.jsx +++ b/client/admin/admin.jsx @@ -4,8 +4,9 @@ const createClass = require('create-react-class'); const BrewUtils = require('./brewUtils/brewUtils.jsx'); const NotificationUtils = require('./notificationUtils/notificationUtils.jsx'); +import AuthorUtils from './authorUtils/authorUtils.jsx'; -const tabGroups = ['brew', 'notifications']; +const tabGroups = ['brew', 'notifications', 'authors']; const Admin = createClass({ getDefaultProps : function() { @@ -39,6 +40,7 @@ const Admin = createClass({ {this.state.currentTab==='brew' && } {this.state.currentTab==='notifications' && } + {this.state.currentTab==='authors' && } ; } diff --git a/client/admin/authorUtils/authorLookup/authorLookup.jsx b/client/admin/authorUtils/authorLookup/authorLookup.jsx new file mode 100644 index 000000000..a239f901f --- /dev/null +++ b/client/admin/authorUtils/authorLookup/authorLookup.jsx @@ -0,0 +1,87 @@ +import './authorLookup.less'; + +import React from 'react'; +import request from 'superagent'; + +const authorLookup = ()=>{ + const [author, setAuthor] = React.useState(''); + const [searching, setSearching] = React.useState(false); + const [results, setResults] = React.useState([]); + + const lookup = async ()=>{ + if(!author) return; + + setSearching(true); + setResults([]); + + const brews = await request.get(`/admin/user/list/${author}`); + setResults(brews.body); + setSearching(false); + }; + + const renderResults = ()=>{ + if(results.length == 0) return <> +

Results

+

None found.

+ ; + + return <> +

{`Results - ${results.length}`}

+ + + + + + + + + + + + {results + .sort((a, b)=>{ // Sort brews from most recently updated + if(a.updatedAt > b.updatedAt) return -1; + return 1; + }) + .map((brew, idx)=>{ + return + + + + + + ; + })} + +
TitleShareEditLast UpdateStorage
{brew.title}{brew.shareId}{brew.editId}{brew.updatedAt}{brew.googleId ? 'Google' : 'Homebrewery'}
+ ; + }; + + const handleKeyPress = (evt)=>{ + if(evt.key === 'Enter') return lookup(); + }; + + const handleChange = (evt)=>{ + setAuthor(evt.target.value); + }; + + return ( +
+
+

Author Lookup

+ +
+
+ {renderResults()} +
+
+ ); +}; + +module.exports = authorLookup; diff --git a/client/admin/authorUtils/authorLookup/authorLookup.less b/client/admin/authorUtils/authorLookup/authorLookup.less new file mode 100644 index 000000000..7f6903aa5 --- /dev/null +++ b/client/admin/authorUtils/authorLookup/authorLookup.less @@ -0,0 +1,48 @@ +.authorLookup { + position : relative; + display : flex; + flex-direction : column; + + .field { + display : flex; + gap : 5px; + align-items : center; + justify-items : stretch; + width : 100%; + margin-bottom : 20px; + + + input { + height : 33px; + padding : 0px 10px; + margin-bottom : unset; + font-family : monospace; + } + + button { + width: 50px; + + i { margin-right : 10px; } + } + } + + table.resultsTable { + * { + border: 1px solid black; + vertical-align: middle; + padding: 5px; + } + + th { + font-weight: bold; + } + + th, td { + text-align: center; + + &:first-of-type { + text-align: left; + } + } + } +} \ No newline at end of file diff --git a/client/admin/authorUtils/authorUtils.jsx b/client/admin/authorUtils/authorUtils.jsx new file mode 100644 index 000000000..a96eea528 --- /dev/null +++ b/client/admin/authorUtils/authorUtils.jsx @@ -0,0 +1,13 @@ +import React from 'react'; + +import AuthorLookup from './authorLookup/authorLookup.jsx'; + +const authorUtils = ()=>{ + return ( +
+ +
+ ); +}; + +module.exports = authorUtils; \ No newline at end of file diff --git a/server/admin.api.js b/server/admin.api.js index 1a39f020b..a7d1d9811 100644 --- a/server/admin.api.js +++ b/server/admin.api.js @@ -93,7 +93,7 @@ router.get('/admin/finduncompressed', mw.adminOnly, (req, res)=>{ /* Cleans `` from the "text" field of a brew */ router.put('/admin/clean/script/:id', asyncHandler(HomebrewAPI.getBrew('admin', false)), async (req, res)=>{ - console.log(`[ADMIN] Cleaning script tags from ShareID ${req.params.id}`); + console.log(`[ADMIN: ${req.account?.username || 'Not Logged In'}] Cleaning script tags from ShareID ${req.params.id}`); function cleanText(text){return text.replaceAll(/(<\/?s)cript/gi, '');}; @@ -114,6 +114,18 @@ router.put('/admin/clean/script/:id', asyncHandler(HomebrewAPI.getBrew('admin', return await HomebrewAPI.updateBrew(req, res); }); +/* Get list of a user's documents */ +router.get('/admin/user/list/:user', mw.adminOnly, async (req, res)=>{ + const username = req.params.user; + const fields = { _id: 0, text: 0, textBin: 0 }; // Remove unnecessary fields from document lists + + console.log(`[ADMIN: ${req.account?.username || 'Not Logged In'}] Get brew list for ${username}`); + + const brews = await HomebrewModel.getByUser(username, true, fields); + + return res.json(brews); +}); + /* Compresses the "text" field of a brew to binary */ router.put('/admin/compress/:id', (req, res)=>{ HomebrewModel.findOne({ _id: req.params.id })