Spaces:
Paused
Paused
| const fsPromises = require('fs').promises; | |
| const storage = require('node-persist'); | |
| const express = require('express'); | |
| const lodash = require('lodash'); | |
| const { jsonParser } = require('../express-common'); | |
| const { checkForNewContent } = require('./content-manager'); | |
| const { | |
| KEY_PREFIX, | |
| toKey, | |
| requireAdminMiddleware, | |
| getUserAvatar, | |
| getAllUserHandles, | |
| getPasswordSalt, | |
| getPasswordHash, | |
| getUserDirectories, | |
| ensurePublicDirectoriesExist, | |
| } = require('../users'); | |
| const { DEFAULT_USER } = require('../constants'); | |
| const router = express.Router(); | |
| router.post('/get', requireAdminMiddleware, jsonParser, async (_request, response) => { | |
| try { | |
| /** @type {import('../users').User[]} */ | |
| const users = await storage.values(x => x.key.startsWith(KEY_PREFIX)); | |
| /** @type {Promise<import('../users').UserViewModel>[]} */ | |
| const viewModelPromises = users | |
| .map(user => new Promise(resolve => { | |
| getUserAvatar(user.handle).then(avatar => | |
| resolve({ | |
| handle: user.handle, | |
| name: user.name, | |
| avatar: avatar, | |
| admin: user.admin, | |
| enabled: user.enabled, | |
| created: user.created, | |
| password: !!user.password, | |
| }), | |
| ); | |
| })); | |
| const viewModels = await Promise.all(viewModelPromises); | |
| viewModels.sort((x, y) => (x.created ?? 0) - (y.created ?? 0)); | |
| return response.json(viewModels); | |
| } catch (error) { | |
| console.error('User list failed:', error); | |
| return response.sendStatus(500); | |
| } | |
| }); | |
| router.post('/disable', requireAdminMiddleware, jsonParser, async (request, response) => { | |
| try { | |
| if (!request.body.handle) { | |
| console.log('Disable user failed: Missing required fields'); | |
| return response.status(400).json({ error: 'Missing required fields' }); | |
| } | |
| if (request.body.handle === request.user.profile.handle) { | |
| console.log('Disable user failed: Cannot disable yourself'); | |
| return response.status(400).json({ error: 'Cannot disable yourself' }); | |
| } | |
| /** @type {import('../users').User} */ | |
| const user = await storage.getItem(toKey(request.body.handle)); | |
| if (!user) { | |
| console.log('Disable user failed: User not found'); | |
| return response.status(404).json({ error: 'User not found' }); | |
| } | |
| user.enabled = false; | |
| await storage.setItem(toKey(request.body.handle), user); | |
| return response.sendStatus(204); | |
| } catch (error) { | |
| console.error('User disable failed:', error); | |
| return response.sendStatus(500); | |
| } | |
| }); | |
| router.post('/enable', requireAdminMiddleware, jsonParser, async (request, response) => { | |
| try { | |
| if (!request.body.handle) { | |
| console.log('Enable user failed: Missing required fields'); | |
| return response.status(400).json({ error: 'Missing required fields' }); | |
| } | |
| /** @type {import('../users').User} */ | |
| const user = await storage.getItem(toKey(request.body.handle)); | |
| if (!user) { | |
| console.log('Enable user failed: User not found'); | |
| return response.status(404).json({ error: 'User not found' }); | |
| } | |
| user.enabled = true; | |
| await storage.setItem(toKey(request.body.handle), user); | |
| return response.sendStatus(204); | |
| } catch (error) { | |
| console.error('User enable failed:', error); | |
| return response.sendStatus(500); | |
| } | |
| }); | |
| router.post('/promote', requireAdminMiddleware, jsonParser, async (request, response) => { | |
| try { | |
| if (!request.body.handle) { | |
| console.log('Promote user failed: Missing required fields'); | |
| return response.status(400).json({ error: 'Missing required fields' }); | |
| } | |
| /** @type {import('../users').User} */ | |
| const user = await storage.getItem(toKey(request.body.handle)); | |
| if (!user) { | |
| console.log('Promote user failed: User not found'); | |
| return response.status(404).json({ error: 'User not found' }); | |
| } | |
| user.admin = true; | |
| await storage.setItem(toKey(request.body.handle), user); | |
| return response.sendStatus(204); | |
| } catch (error) { | |
| console.error('User promote failed:', error); | |
| return response.sendStatus(500); | |
| } | |
| }); | |
| router.post('/demote', requireAdminMiddleware, jsonParser, async (request, response) => { | |
| try { | |
| if (!request.body.handle) { | |
| console.log('Demote user failed: Missing required fields'); | |
| return response.status(400).json({ error: 'Missing required fields' }); | |
| } | |
| if (request.body.handle === request.user.profile.handle) { | |
| console.log('Demote user failed: Cannot demote yourself'); | |
| return response.status(400).json({ error: 'Cannot demote yourself' }); | |
| } | |
| /** @type {import('../users').User} */ | |
| const user = await storage.getItem(toKey(request.body.handle)); | |
| if (!user) { | |
| console.log('Demote user failed: User not found'); | |
| return response.status(404).json({ error: 'User not found' }); | |
| } | |
| user.admin = false; | |
| await storage.setItem(toKey(request.body.handle), user); | |
| return response.sendStatus(204); | |
| } catch (error) { | |
| console.error('User demote failed:', error); | |
| return response.sendStatus(500); | |
| } | |
| }); | |
| router.post('/create', requireAdminMiddleware, jsonParser, async (request, response) => { | |
| try { | |
| if (!request.body.handle || !request.body.name) { | |
| console.log('Create user failed: Missing required fields'); | |
| return response.status(400).json({ error: 'Missing required fields' }); | |
| } | |
| const handles = await getAllUserHandles(); | |
| const handle = lodash.kebabCase(String(request.body.handle).toLowerCase().trim()); | |
| if (!handle) { | |
| console.log('Create user failed: Invalid handle'); | |
| return response.status(400).json({ error: 'Invalid handle' }); | |
| } | |
| if (handles.some(x => x === handle)) { | |
| console.log('Create user failed: User with that handle already exists'); | |
| return response.status(409).json({ error: 'User already exists' }); | |
| } | |
| const salt = getPasswordSalt(); | |
| const password = request.body.password ? getPasswordHash(request.body.password, salt) : ''; | |
| const newUser = { | |
| handle: handle, | |
| name: request.body.name || 'Anonymous', | |
| created: Date.now(), | |
| password: password, | |
| salt: salt, | |
| admin: !!request.body.admin, | |
| enabled: true, | |
| }; | |
| await storage.setItem(toKey(handle), newUser); | |
| // Create user directories | |
| console.log('Creating data directories for', newUser.handle); | |
| await ensurePublicDirectoriesExist(); | |
| const directories = getUserDirectories(newUser.handle); | |
| await checkForNewContent([directories]); | |
| return response.json({ handle: newUser.handle }); | |
| } catch (error) { | |
| console.error('User create failed:', error); | |
| return response.sendStatus(500); | |
| } | |
| }); | |
| router.post('/delete', requireAdminMiddleware, jsonParser, async (request, response) => { | |
| try { | |
| if (!request.body.handle) { | |
| console.log('Delete user failed: Missing required fields'); | |
| return response.status(400).json({ error: 'Missing required fields' }); | |
| } | |
| if (request.body.handle === request.user.profile.handle) { | |
| console.log('Delete user failed: Cannot delete yourself'); | |
| return response.status(400).json({ error: 'Cannot delete yourself' }); | |
| } | |
| if (request.body.handle === DEFAULT_USER.handle) { | |
| console.log('Delete user failed: Cannot delete default user'); | |
| return response.status(400).json({ error: 'Sorry, but the default user cannot be deleted. It is required as a fallback.' }); | |
| } | |
| await storage.removeItem(toKey(request.body.handle)); | |
| if (request.body.purge) { | |
| const directories = getUserDirectories(request.body.handle); | |
| console.log('Deleting data directories for', request.body.handle); | |
| await fsPromises.rm(directories.root, { recursive: true, force: true }); | |
| } | |
| return response.sendStatus(204); | |
| } catch (error) { | |
| console.error('User delete failed:', error); | |
| return response.sendStatus(500); | |
| } | |
| }); | |
| router.post('/slugify', requireAdminMiddleware, jsonParser, async (request, response) => { | |
| try { | |
| if (!request.body.text) { | |
| console.log('Slugify failed: Missing required fields'); | |
| return response.status(400).json({ error: 'Missing required fields' }); | |
| } | |
| const text = lodash.kebabCase(String(request.body.text).toLowerCase().trim()); | |
| return response.send(text); | |
| } catch (error) { | |
| console.error('Slugify failed:', error); | |
| return response.sendStatus(500); | |
| } | |
| }); | |
| module.exports = { | |
| router, | |
| }; | |