@underpostnet/underpost
Version:
Underpost Platform — end-to-end CI/CD and application-delivery toolchain CLI. Covers bare metal, Kubernetes, K3s, kubeadm, LXD, container/image orchestration, secrets, databases, cron jobs, monitoring, SSH, runners, PWA + Workbox delivery, and release orc
368 lines (328 loc) • 11.2 kB
JavaScript
import { hashPassword } from '../../server/auth.js';
import fs from 'fs-extra';
import { loggerFactory } from '../../server/logger.js';
import { UserController } from './user.controller.js';
import express from 'express';
import { DataBaseProviderService } from '../../db/DataBaseProvider.js';
const logger = loggerFactory(import.meta);
class UserRouter {
/**
* @param {import('../types.js').RouterOptions} options
* @returns {import('express').Router}
*/
static router(options) {
const router = express.Router();
// Admin user seed — fire-and-forget async on first router mount.
(async () => {
try {
const User = DataBaseProviderService.getModel('user', options);
const adminUser = await User.findOne({ role: 'admin' });
if (!adminUser) {
const defaultPassword = process.env.DEFAULT_ADMIN_PASSWORD || 'changethis';
const hashedPassword = await hashPassword(defaultPassword);
const result = await User.create({
username: 'admin',
email: process.env.DEFAULT_ADMIN_EMAIL || 'admin@' + options.host,
password: hashedPassword,
role: 'admin',
emailConfirmed: true,
publicKey: [],
});
logger.warn('Default admin user created. Please change the default password immediately!', {
username: result._doc.username,
email: result._doc.email,
role: result._doc.role,
});
}
} catch (error) {
logger.error('Error checking/creating admin user', { error: error.message });
}
// Cache mailer images into options.png so route handlers can serve them
// without re-reading the filesystem on every request.
options.png = {
buffer: {
'invalid-token': fs.readFileSync(`./src/client/public/default/assets/mailer/api-user-invalid-token.png`),
recover: fs.readFileSync(`./src/client/public/default/assets/mailer/api-user-recover.png`),
check: fs.readFileSync(`./src/client/public/default/assets/mailer/api-user-check.png`),
avatar: fs.readFileSync(`./src/client/public/default/assets/mailer/api-user-default-avatar.png`),
},
header: (res, req) => {
res.set('Cross-Origin-Resource-Policy', 'cross-origin');
res.set('Access-Control-Allow-Headers', '*');
if (req && req.headers && req.headers.origin) {
res.set('Access-Control-Allow-Origin', req.headers.origin);
} else res.setHeader('Access-Control-Allow-Origin', '*');
res.set('Content-Type', 'image/png');
},
};
})();
router.post(`/mailer/:id`, options.authMiddleware, async (req, res) => {
/*
#swagger.ignore = true
*/
return await UserController.post(req, res, options);
});
router.get(`/assets/:id`, async (req, res) => {
/*
#swagger.ignore = true
*/
return await UserController.get(req, res, options);
});
router.get(`/mailer/:id`, async (req, res) => {
/*
#swagger.ignore = true
*/
return await UserController.get(req, res, options);
});
router.get(`/email/:email`, options.authMiddleware, async (req, res) => {
/*
#swagger.ignore = true
*/
return await UserController.get(req, res, options);
});
router.post(`/:id`, async (req, res) => {
/*
#swagger.ignore = true
*/
return await UserController.post(req, res, options);
});
// #swagger.start
/*
#swagger.auto = false
#swagger.tags = ['user']
#swagger.summary = 'Log in'
#swagger.description = 'This endpoint get a JWT for authenticated user'
#swagger.path = '/user/auth'
#swagger.method = 'post'
#swagger.responses[200] = {
description: 'User created successfully',
content: {
'application/json': {
schema: {
$ref: '#/components/schemas/userResponse'
}
}
}
}
#swagger.responses[400] = {
description: 'Bad request. Please check the input data',
content: {
'application/json': {
schema: {
$ref: '#/components/schemas/userBadRequestResponse'
}
}
}
}
*/
// #swagger.end
router.post(`/`, async (req, res) => {
/*
#swagger.auto = false
#swagger.tags = ['user']
#swagger.summary = 'Create user'
#swagger.description = 'This endpoint will create a new user account'
#swagger.path = '/user'
#swagger.method = 'post'
#swagger.responses[200] = {
description: 'User created successfully',
content: {
'application/json': {
schema: {
$ref: '#/components/schemas/userResponse'
}
}
}
}
#swagger.responses[400] = {
description: 'Bad request. Please check the input data',
content: {
'application/json': {
schema: {
$ref: '#/components/schemas/userBadRequestResponse'
}
}
}
}
*/
return await UserController.post(req, res, options);
});
router.get(`/recover/:id`, async (req, res) => {
/*
#swagger.ignore = true
*/
return await UserController.get(req, res, options);
});
router.get(`/u/:username`, async (req, res) => {
/*
#swagger.ignore = true
*/
return await UserController.get(req, res, options);
});
router.get(`/:id`, options.authMiddleware, async (req, res) => {
/*
#swagger.auto = false
#swagger.tags = ['user']
#swagger.summary = 'Get user data by ID'
#swagger.description = 'This endpoint get user data by ID'
#swagger.path = '/user/{id}'
#swagger.method = 'get'
#swagger.produces = ['application/json']
#swagger.consumes = ['application/json']
#swagger.security = [{
'bearerAuth': []
}]
#swagger.parameters['id'] = {
in: 'path',
description: 'User ID',
required: true,
type: 'string'
}
#swagger.responses[200] = {
description: 'get user successfully',
content: {
'application/json': {
schema: {
$ref: '#/components/schemas/userGetResponse'
}
}
}
}
#swagger.responses[400] = {
description: 'Bad request. Please check the input data',
content: {
'application/json': {
schema: {
$ref: '#/components/schemas/userBadRequestResponse'
}
}
}
}
*/
return await UserController.get(req, res, options);
});
router.get(`/`, options.authMiddleware, async (req, res) => {
/*
#swagger.ignore = true
*/
return await UserController.get(req, res, options);
});
router.put(`/recover/:id`, async (req, res) => {
/*
#swagger.ignore = true
*/
return await UserController.put(req, res, options);
});
router.put(`/profile-image/:id`, options.authMiddleware, async (req, res) => {
/*
#swagger.ignore = true
*/
return await UserController.put(req, res, options);
});
router.put(`/:id`, options.authMiddleware, async (req, res) => {
/*
#swagger.auto = false
#swagger.tags = ['user']
#swagger.summary = 'Update user data by ID'
#swagger.description = 'This endpoint will update user data by ID'
#swagger.path = '/user/{id}'
#swagger.method = 'put'
#swagger.security = [{
'bearerAuth': []
}]
#swagger.parameters['id'] = {
in: 'path',
description: 'User ID',
required: true,
type: 'string'
}
#swagger.responses[200] = {
description: 'User updated successfully',
content: {
'application/json': {
schema: {
$ref: '#/components/schemas/userUpdateResponse'
}
}
}
}
#swagger.responses[400] = {
description: 'Bad request. Please check the input data',
content: {
'application/json': {
schema: {
$ref: '#/components/schemas/userBadRequestResponse'
}
}
}
}
*/
return await UserController.put(req, res, options);
});
router.put(`/`, options.authMiddleware, async (req, res) => {
/*
#swagger.ignore = true
*/
return await UserController.put(req, res, options);
});
router.delete(`/:id`, options.authMiddleware, async (req, res) => {
/*
#swagger.auto = false
#swagger.tags = ['user']
#swagger.summary = 'Delete user data by ID'
#swagger.description = 'This endpoint deletes user data by ID, the path ID must match with the ID of the authenticated user'
#swagger.path = '/user/{id}'
#swagger.method = 'delete'
#swagger.produces = ['application/json']
#swagger.consumes = ['application/json']
#swagger.security = [{
'bearerAuth': []
}]
#swagger.parameters['id'] = {
in: 'path',
description: 'User ID',
required: true,
type: 'string'
}
#swagger.responses[200] = {
description: 'get user successfully',
content: {
'application/json': {
schema: {
$ref: '#/components/schemas/userGetResponse'
}
}
}
}
#swagger.responses[400] = {
description: 'Bad request. Please check the input data',
content: {
'application/json': {
schema: {
$ref: '#/components/schemas/userBadRequestResponse'
}
}
}
}
*/
return await UserController.delete(req, res, options);
});
router.delete(`/`, options.authMiddleware, async (req, res) => {
/*
#swagger.ignore = true
*/
return await UserController.delete(req, res, options);
});
// Username public profile redirect — registered directly on the app so the
// /u/:username shortlink works outside the API path prefix.
options.app.get(`${options.path === '/' ? '' : options.path}/u/:username`, async (req, res, next) => {
/*
#swagger.ignore = true
*/
return res.redirect(`${options.path === '/' ? '' : options.path}/u?cid=${req.params.username}`);
});
return router;
}
}
const ApiRouter = (options) => UserRouter.router(options);
export { ApiRouter, UserRouter };