mirror of
https://github.com/Unleash/unleash.git
synced 2025-07-26 13:48:33 +02:00
fix: convert xtract-user.js to typescript
This commit is contained in:
parent
83bfcde934
commit
e2177da114
@ -1,7 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
function extractUsername(req) {
|
||||
return req.user ? req.user.email || req.user.username : 'unknown';
|
||||
}
|
||||
|
||||
module.exports = extractUsername;
|
@ -5,12 +5,13 @@ import { IUnleashServices } from '../../types/services';
|
||||
import { Logger } from '../../logger';
|
||||
import AddonService from '../../services/addon-service';
|
||||
|
||||
import extractUser from '../../extract-user';
|
||||
import { extractUsername } from '../../util/extract-user';
|
||||
import {
|
||||
CREATE_ADDON,
|
||||
UPDATE_ADDON,
|
||||
DELETE_ADDON,
|
||||
} from '../../types/permissions';
|
||||
import { IAuthRequest } from '../unleash-types';
|
||||
|
||||
class AddonController extends Controller {
|
||||
private logger: Logger;
|
||||
@ -48,30 +49,30 @@ class AddonController extends Controller {
|
||||
}
|
||||
|
||||
async updateAddon(
|
||||
req: Request<{ id: number }, any, any, any>,
|
||||
req: IAuthRequest<{ id: number }, any, any, any>,
|
||||
res: Response,
|
||||
): Promise<void> {
|
||||
const { id } = req.params;
|
||||
const createdBy = extractUser(req);
|
||||
const createdBy = extractUsername(req);
|
||||
const data = req.body;
|
||||
|
||||
const addon = await this.addonService.updateAddon(id, data, createdBy);
|
||||
res.status(200).json(addon);
|
||||
}
|
||||
|
||||
async createAddon(req: Request, res: Response): Promise<void> {
|
||||
const createdBy = extractUser(req);
|
||||
async createAddon(req: IAuthRequest, res: Response): Promise<void> {
|
||||
const createdBy = extractUsername(req);
|
||||
const data = req.body;
|
||||
const addon = await this.addonService.createAddon(data, createdBy);
|
||||
res.status(201).json(addon);
|
||||
}
|
||||
|
||||
async deleteAddon(
|
||||
req: Request<{ id: number }, any, any, any>,
|
||||
req: IAuthRequest<{ id: number }, any, any, any>,
|
||||
res: Response,
|
||||
): Promise<void> {
|
||||
const { id } = req.params;
|
||||
const username = extractUser(req);
|
||||
const username = extractUsername(req);
|
||||
await this.addonService.removeAddon(id, username);
|
||||
res.status(200).end();
|
||||
}
|
||||
|
@ -1,13 +1,14 @@
|
||||
import { Request, Response } from 'express';
|
||||
import { Response } from 'express';
|
||||
import { IUnleashConfig } from '../../types/option';
|
||||
import { IUnleashServices } from '../../types/services';
|
||||
import { Logger } from '../../logger';
|
||||
|
||||
import Controller from '../controller';
|
||||
|
||||
import extractUser from '../../extract-user';
|
||||
import { extractUsername } from '../../util/extract-user';
|
||||
import { DELETE_FEATURE, UPDATE_FEATURE } from '../../types/permissions';
|
||||
import FeatureToggleServiceV2 from '../../services/feature-toggle-service-v2';
|
||||
import { IAuthRequest } from '../unleash-types';
|
||||
|
||||
export default class ArchiveController extends Controller {
|
||||
private readonly logger: Logger;
|
||||
@ -42,18 +43,18 @@ export default class ArchiveController extends Controller {
|
||||
}
|
||||
|
||||
async deleteFeature(
|
||||
req: Request<any, { featureName: string }, any, any>,
|
||||
req: IAuthRequest<any, { featureName: string }, any, any>,
|
||||
res: Response,
|
||||
): Promise<void> {
|
||||
const { featureName } = req.params;
|
||||
const user = extractUser(req);
|
||||
const user = extractUsername(req);
|
||||
await this.featureService.deleteFeature(featureName, user);
|
||||
res.status(200).end();
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
||||
async reviveFeatureToggle(req, res): Promise<void> {
|
||||
const userName = extractUser(req);
|
||||
async reviveFeatureToggle(req: IAuthRequest, res: Response): Promise<void> {
|
||||
const userName = extractUsername(req);
|
||||
const { featureName } = req.params;
|
||||
await this.featureService.reviveToggle(featureName, userName);
|
||||
res.status(200).end();
|
||||
|
@ -2,7 +2,7 @@ import { Request, Response } from 'express';
|
||||
|
||||
import Controller from '../controller';
|
||||
|
||||
import extractUser from '../../extract-user';
|
||||
import { extractUsername } from '../../util/extract-user';
|
||||
|
||||
import {
|
||||
CREATE_CONTEXT_FIELD,
|
||||
@ -13,6 +13,7 @@ import { IUnleashConfig } from '../../types/option';
|
||||
import { IUnleashServices } from '../../types/services';
|
||||
import ContextService from '../../services/context-service';
|
||||
import { Logger } from '../../logger';
|
||||
import { IAuthRequest } from '../unleash-types';
|
||||
|
||||
class ContextController extends Controller {
|
||||
private logger: Logger;
|
||||
@ -60,17 +61,17 @@ class ContextController extends Controller {
|
||||
}
|
||||
}
|
||||
|
||||
async createContextField(req: Request, res: Response): Promise<void> {
|
||||
async createContextField(req: IAuthRequest, res: Response): Promise<void> {
|
||||
const value = req.body;
|
||||
const userName = extractUser(req);
|
||||
const userName = extractUsername(req);
|
||||
|
||||
await this.contextService.createContextField(value, userName);
|
||||
res.status(201).end();
|
||||
}
|
||||
|
||||
async updateContextField(req: Request, res: Response): Promise<void> {
|
||||
async updateContextField(req: IAuthRequest, res: Response): Promise<void> {
|
||||
const name = req.params.contextField;
|
||||
const userName = extractUser(req);
|
||||
const userName = extractUsername(req);
|
||||
const contextField = req.body;
|
||||
|
||||
contextField.name = name;
|
||||
@ -79,9 +80,9 @@ class ContextController extends Controller {
|
||||
res.status(200).end();
|
||||
}
|
||||
|
||||
async deleteContextField(req: Request, res: Response): Promise<void> {
|
||||
async deleteContextField(req: IAuthRequest, res: Response): Promise<void> {
|
||||
const name = req.params.contextField;
|
||||
const userName = extractUser(req);
|
||||
const userName = extractUsername(req);
|
||||
|
||||
await this.contextService.deleteContextField(name, userName);
|
||||
res.status(200).end();
|
||||
|
@ -3,7 +3,7 @@ import { Request, Response } from 'express';
|
||||
|
||||
import Controller from '../controller';
|
||||
|
||||
import extractUser from '../../extract-user';
|
||||
import { extractUsername } from '../../util/extract-user';
|
||||
import {
|
||||
UPDATE_FEATURE,
|
||||
DELETE_FEATURE,
|
||||
@ -17,6 +17,7 @@ import { featureSchema, querySchema } from '../../schema/feature-schema';
|
||||
import { IFeatureToggleQuery } from '../../types/model';
|
||||
import FeatureTagService from '../../services/feature-tag-service';
|
||||
import { GLOBAL_ENV } from '../../types/environment';
|
||||
import { IAuthRequest } from '../unleash-types';
|
||||
|
||||
const version = 1;
|
||||
|
||||
@ -126,9 +127,9 @@ class FeatureController extends Controller {
|
||||
res.json({ version, tags });
|
||||
}
|
||||
|
||||
async addTag(req: Request, res: Response): Promise<void> {
|
||||
async addTag(req: IAuthRequest, res: Response): Promise<void> {
|
||||
const { featureName } = req.params;
|
||||
const userName = extractUser(req);
|
||||
const userName = extractUsername(req);
|
||||
const tag = await this.featureTagService.addTag(
|
||||
featureName,
|
||||
req.body,
|
||||
@ -138,9 +139,9 @@ class FeatureController extends Controller {
|
||||
}
|
||||
|
||||
// TODO
|
||||
async removeTag(req: Request, res: Response): Promise<void> {
|
||||
async removeTag(req: IAuthRequest, res: Response): Promise<void> {
|
||||
const { featureName, type, value } = req.params;
|
||||
const userName = extractUser(req);
|
||||
const userName = extractUsername(req);
|
||||
await this.featureTagService.removeTag(
|
||||
featureName,
|
||||
{ type, value },
|
||||
@ -156,8 +157,8 @@ class FeatureController extends Controller {
|
||||
res.status(200).end();
|
||||
}
|
||||
|
||||
async createToggle(req: Request, res: Response): Promise<void> {
|
||||
const userName = extractUser(req);
|
||||
async createToggle(req: IAuthRequest, res: Response): Promise<void> {
|
||||
const userName = extractUsername(req);
|
||||
const toggle = req.body;
|
||||
|
||||
const validatedToggle = await featureSchema.validateAsync(toggle);
|
||||
@ -191,9 +192,9 @@ class FeatureController extends Controller {
|
||||
});
|
||||
}
|
||||
|
||||
async updateToggle(req: Request, res: Response): Promise<void> {
|
||||
async updateToggle(req: IAuthRequest, res: Response): Promise<void> {
|
||||
const { featureName } = req.params;
|
||||
const userName = extractUser(req);
|
||||
const userName = extractUsername(req);
|
||||
const updatedFeature = req.body;
|
||||
|
||||
updatedFeature.name = featureName;
|
||||
@ -236,8 +237,8 @@ class FeatureController extends Controller {
|
||||
|
||||
// TODO: remove?
|
||||
// Kept to keep backward compatibility
|
||||
async toggle(req: Request, res: Response): Promise<void> {
|
||||
const userName = extractUser(req);
|
||||
async toggle(req: IAuthRequest, res: Response): Promise<void> {
|
||||
const userName = extractUsername(req);
|
||||
const { featureName } = req.params;
|
||||
const projectId = await this.featureService2.getProjectId(featureName);
|
||||
const feature = await this.featureService2.toggle(
|
||||
@ -249,9 +250,9 @@ class FeatureController extends Controller {
|
||||
res.status(200).json(feature);
|
||||
}
|
||||
|
||||
async toggleOn(req: Request, res: Response): Promise<void> {
|
||||
async toggleOn(req: IAuthRequest, res: Response): Promise<void> {
|
||||
const { featureName } = req.params;
|
||||
const userName = extractUser(req);
|
||||
const userName = extractUsername(req);
|
||||
const projectId = await this.featureService2.getProjectId(featureName);
|
||||
const feature = await this.featureService2.updateEnabled(
|
||||
projectId,
|
||||
@ -263,9 +264,9 @@ class FeatureController extends Controller {
|
||||
res.json(feature);
|
||||
}
|
||||
|
||||
async toggleOff(req: Request, res: Response): Promise<void> {
|
||||
async toggleOff(req: IAuthRequest, res: Response): Promise<void> {
|
||||
const { featureName } = req.params;
|
||||
const userName = extractUser(req);
|
||||
const userName = extractUsername(req);
|
||||
const projectId = await this.featureService2.getProjectId(featureName);
|
||||
const feature = await this.featureService2.updateEnabled(
|
||||
projectId,
|
||||
@ -277,29 +278,28 @@ class FeatureController extends Controller {
|
||||
res.json(feature);
|
||||
}
|
||||
|
||||
async staleOn(req: Request, res: Response): Promise<void> {
|
||||
async staleOn(req: IAuthRequest, res: Response): Promise<void> {
|
||||
const { featureName } = req.params;
|
||||
const userName = extractUser(req);
|
||||
const userName = extractUsername(req);
|
||||
await this.featureService2.updateStale(featureName, true, userName);
|
||||
const feature = await this.getLegacyFeatureToggle(featureName);
|
||||
res.json(feature).end();
|
||||
}
|
||||
|
||||
async staleOff(req: Request, res: Response): Promise<void> {
|
||||
async staleOff(req: IAuthRequest, res: Response): Promise<void> {
|
||||
const { featureName } = req.params;
|
||||
const userName = extractUser(req);
|
||||
const userName = extractUsername(req);
|
||||
await this.featureService2.updateStale(featureName, false, userName);
|
||||
const feature = await this.getLegacyFeatureToggle(featureName);
|
||||
res.json(feature).end();
|
||||
}
|
||||
|
||||
async archiveToggle(req: Request, res: Response): Promise<void> {
|
||||
async archiveToggle(req: IAuthRequest, res: Response): Promise<void> {
|
||||
const { featureName } = req.params;
|
||||
const userName = extractUser(req);
|
||||
const userName = extractUsername(req);
|
||||
|
||||
await this.featureService2.archiveToggle(featureName, userName);
|
||||
res.status(200).end();
|
||||
}
|
||||
}
|
||||
export default FeatureController;
|
||||
module.exports = FeatureController;
|
||||
|
@ -15,7 +15,8 @@ import {
|
||||
IConstraint,
|
||||
IStrategyConfig,
|
||||
} from '../../../types/model';
|
||||
import extractUsername from '../../../extract-user';
|
||||
import { extractUsername } from '../../../util/extract-user';
|
||||
import { IAuthRequest } from '../../unleash-types';
|
||||
|
||||
interface FeatureStrategyParams {
|
||||
projectId: string;
|
||||
@ -98,7 +99,7 @@ export default class ProjectFeaturesController extends Controller {
|
||||
}
|
||||
|
||||
async createFeature(
|
||||
req: Request<ProjectParam, any, FeatureToggleDTO, any>,
|
||||
req: IAuthRequest<ProjectParam, any, FeatureToggleDTO, any>,
|
||||
res: Response,
|
||||
): Promise<void> {
|
||||
const { projectId } = req.params;
|
||||
@ -121,7 +122,7 @@ export default class ProjectFeaturesController extends Controller {
|
||||
}
|
||||
|
||||
async updateFeature(
|
||||
req: Request<ProjectParam, any, FeatureToggleDTO, any>,
|
||||
req: IAuthRequest<ProjectParam, any, FeatureToggleDTO, any>,
|
||||
res: Response,
|
||||
): Promise<void> {
|
||||
const { projectId } = req.params;
|
||||
@ -136,7 +137,7 @@ export default class ProjectFeaturesController extends Controller {
|
||||
}
|
||||
|
||||
async patchFeature(
|
||||
req: Request<
|
||||
req: IAuthRequest<
|
||||
{ projectId: string; featureName: string },
|
||||
any,
|
||||
Operation[],
|
||||
@ -160,7 +161,12 @@ export default class ProjectFeaturesController extends Controller {
|
||||
|
||||
// TODO: validate projectId
|
||||
async archiveFeature(
|
||||
req: Request<{ projectId: string; featureName: string }, any, any, any>,
|
||||
req: IAuthRequest<
|
||||
{ projectId: string; featureName: string },
|
||||
any,
|
||||
any,
|
||||
any
|
||||
>,
|
||||
res: Response,
|
||||
): Promise<void> {
|
||||
const { featureName } = req.params;
|
||||
@ -183,7 +189,7 @@ export default class ProjectFeaturesController extends Controller {
|
||||
}
|
||||
|
||||
async toggleEnvironmentOn(
|
||||
req: Request<FeatureStrategyParams, any, any, any>,
|
||||
req: IAuthRequest<FeatureStrategyParams, any, any, any>,
|
||||
res: Response,
|
||||
): Promise<void> {
|
||||
const { featureName, environment, projectId } = req.params;
|
||||
@ -198,7 +204,7 @@ export default class ProjectFeaturesController extends Controller {
|
||||
}
|
||||
|
||||
async toggleEnvironmentOff(
|
||||
req: Request<FeatureStrategyParams, any, any, any>,
|
||||
req: IAuthRequest<FeatureStrategyParams, any, any, any>,
|
||||
res: Response,
|
||||
): Promise<void> {
|
||||
const { featureName, environment, projectId } = req.params;
|
||||
|
@ -5,11 +5,12 @@ import multer from 'multer';
|
||||
import { Request, Response } from 'express';
|
||||
import Controller from '../controller';
|
||||
import { ADMIN } from '../../types/permissions';
|
||||
import extractUser from '../../extract-user';
|
||||
import { extractUsername } from '../../util/extract-user';
|
||||
import { IUnleashConfig } from '../../types/option';
|
||||
import { IUnleashServices } from '../../types/services';
|
||||
import { Logger } from '../../logger';
|
||||
import StateService from '../../services/state-service';
|
||||
import { IAuthRequest } from '../unleash-types';
|
||||
|
||||
const upload = multer({ limits: { fileSize: 5242880 } });
|
||||
const paramToBool = (param, def) => {
|
||||
@ -38,8 +39,8 @@ class StateController extends Controller {
|
||||
this.get('/export', this.export, ADMIN);
|
||||
}
|
||||
|
||||
async import(req: Request, res: Response): Promise<void> {
|
||||
const userName = extractUser(req);
|
||||
async import(req: IAuthRequest, res: Response): Promise<void> {
|
||||
const userName = extractUsername(req);
|
||||
const { drop, keep } = req.query;
|
||||
// TODO: Should override request type so file is a type on request
|
||||
let data;
|
||||
|
@ -3,15 +3,15 @@ import { IUnleashServices } from '../../types/services';
|
||||
import StrategyService from '../../services/strategy-service';
|
||||
import { Logger } from '../../logger';
|
||||
|
||||
const Controller = require('../controller');
|
||||
import Controller from '../controller';
|
||||
|
||||
const extractUser = require('../../extract-user');
|
||||
const { handleErrors } = require('../util');
|
||||
const {
|
||||
import { extractUsername } from '../../util/extract-user';
|
||||
import { handleErrors } from '../util';
|
||||
import {
|
||||
DELETE_STRATEGY,
|
||||
CREATE_STRATEGY,
|
||||
UPDATE_STRATEGY,
|
||||
} = require('../../types/permissions');
|
||||
} from '../../types/permissions';
|
||||
|
||||
const version = 1;
|
||||
|
||||
@ -69,7 +69,7 @@ class StrategyController extends Controller {
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
||||
async removeStrategy(req, res): Promise<void> {
|
||||
const strategyName = req.params.name;
|
||||
const userName = extractUser(req);
|
||||
const userName = extractUsername(req);
|
||||
|
||||
try {
|
||||
await this.strategyService.removeStrategy(strategyName, userName);
|
||||
@ -81,7 +81,7 @@ class StrategyController extends Controller {
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
||||
async createStrategy(req, res): Promise<void> {
|
||||
const userName = extractUser(req);
|
||||
const userName = extractUsername(req);
|
||||
try {
|
||||
await this.strategyService.createStrategy(req.body, userName);
|
||||
res.status(201).end();
|
||||
@ -92,7 +92,7 @@ class StrategyController extends Controller {
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
||||
async updateStrategy(req, res): Promise<void> {
|
||||
const userName = extractUser(req);
|
||||
const userName = extractUsername(req);
|
||||
try {
|
||||
await this.strategyService.updateStrategy(req.body, userName);
|
||||
res.status(200).end();
|
||||
@ -103,7 +103,7 @@ class StrategyController extends Controller {
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
||||
async deprecateStrategy(req, res): Promise<void> {
|
||||
const userName = extractUser(req);
|
||||
const userName = extractUsername(req);
|
||||
const { strategyName } = req.params;
|
||||
if (strategyName === 'default') {
|
||||
res.status(403).end();
|
||||
@ -122,7 +122,7 @@ class StrategyController extends Controller {
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
||||
async reactivateStrategy(req, res): Promise<void> {
|
||||
const userName = extractUser(req);
|
||||
const userName = extractUsername(req);
|
||||
const { strategyName } = req.params;
|
||||
try {
|
||||
await this.strategyService.reactivateStrategy(
|
||||
@ -136,4 +136,3 @@ class StrategyController extends Controller {
|
||||
}
|
||||
}
|
||||
export default StrategyController;
|
||||
module.exports = StrategyController;
|
||||
|
@ -2,11 +2,12 @@ import { Request, Response } from 'express';
|
||||
import Controller from '../controller';
|
||||
|
||||
import { UPDATE_FEATURE } from '../../types/permissions';
|
||||
import extractUsername from '../../extract-user';
|
||||
import { extractUsername } from '../../util/extract-user';
|
||||
import { IUnleashConfig } from '../../types/option';
|
||||
import { IUnleashServices } from '../../types/services';
|
||||
import TagTypeService from '../../services/tag-type-service';
|
||||
import { Logger } from '../../logger';
|
||||
import { IAuthRequest } from '../unleash-types';
|
||||
|
||||
const version = 1;
|
||||
|
||||
@ -40,7 +41,7 @@ class TagTypeController extends Controller {
|
||||
res.status(200).json({ valid: true, tagType: req.body });
|
||||
}
|
||||
|
||||
async createTagType(req: Request, res: Response): Promise<void> {
|
||||
async createTagType(req: IAuthRequest, res: Response): Promise<void> {
|
||||
const userName = extractUsername(req);
|
||||
const tagType = await this.tagTypeService.createTagType(
|
||||
req.body,
|
||||
@ -49,7 +50,7 @@ class TagTypeController extends Controller {
|
||||
res.status(201).json(tagType);
|
||||
}
|
||||
|
||||
async updateTagType(req: Request, res: Response): Promise<void> {
|
||||
async updateTagType(req: IAuthRequest, res: Response): Promise<void> {
|
||||
const { description, icon } = req.body;
|
||||
const { name } = req.params;
|
||||
const userName = extractUsername(req);
|
||||
@ -68,7 +69,7 @@ class TagTypeController extends Controller {
|
||||
res.json({ version, tagType });
|
||||
}
|
||||
|
||||
async deleteTagType(req: Request, res: Response): Promise<void> {
|
||||
async deleteTagType(req: IAuthRequest, res: Response): Promise<void> {
|
||||
const { name } = req.params;
|
||||
const userName = extractUsername(req);
|
||||
await this.tagTypeService.deleteTagType(name, userName);
|
||||
|
@ -7,7 +7,8 @@ import { Logger } from '../../logger';
|
||||
import Controller from '../controller';
|
||||
|
||||
import { UPDATE_FEATURE } from '../../types/permissions';
|
||||
import extractUsername from '../../extract-user';
|
||||
import { extractUsername } from '../../util/extract-user';
|
||||
import { IAuthRequest } from '../unleash-types';
|
||||
|
||||
const version = 1;
|
||||
|
||||
@ -47,13 +48,13 @@ class TagController extends Controller {
|
||||
res.json({ version, tag });
|
||||
}
|
||||
|
||||
async createTag(req: Request, res: Response): Promise<void> {
|
||||
async createTag(req: IAuthRequest, res: Response): Promise<void> {
|
||||
const userName = extractUsername(req);
|
||||
await this.tagService.createTag(req.body, userName);
|
||||
res.status(201).end();
|
||||
}
|
||||
|
||||
async deleteTag(req: Request, res: Response): Promise<void> {
|
||||
async deleteTag(req: IAuthRequest, res: Response): Promise<void> {
|
||||
const { type, value } = req.params;
|
||||
const userName = extractUsername(req);
|
||||
await this.tagService.deleteTag({ type, value }, userName);
|
||||
@ -61,4 +62,3 @@ class TagController extends Controller {
|
||||
}
|
||||
}
|
||||
export default TagController;
|
||||
module.exports = TagController;
|
||||
|
@ -1,12 +1,11 @@
|
||||
import { Request } from 'express';
|
||||
import * as core from 'express-serve-static-core';
|
||||
import User from '../types/user';
|
||||
|
||||
export interface IAuthRequest<
|
||||
PARAM = core.ParamsDictionary,
|
||||
PARAM = any,
|
||||
ResBody = any,
|
||||
ReqBody = any,
|
||||
ReqQuery = core.Query,
|
||||
ReqQuery = any,
|
||||
> extends Request<PARAM, ResBody, ReqBody, ReqQuery> {
|
||||
user: User;
|
||||
logout: () => void;
|
||||
|
5
src/lib/util/extract-user.ts
Normal file
5
src/lib/util/extract-user.ts
Normal file
@ -0,0 +1,5 @@
|
||||
import { IAuthRequest } from '../server-impl';
|
||||
|
||||
export function extractUsername(req: IAuthRequest): string {
|
||||
return req.user ? req.user.email || req.user.username : 'unknown';
|
||||
}
|
Loading…
Reference in New Issue
Block a user