add user preferences endpoint
This commit is contained in:
parent
2bbe798097
commit
5632eaffdf
|
@ -2,20 +2,23 @@ import { Module } from '@nestjs/common';
|
|||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
import { EarlyConfigModule } from '../../config/early/earlyconfig.module';
|
||||
import { ESysPreferenceBackend } from '../../models/entities/syspreference.entity';
|
||||
import { EUsrPreferenceBackend } from '../../models/entities/usrpreference.entity';
|
||||
import { PreferenceCommonService } from './preferencecommon.service';
|
||||
import { PreferenceDefaultsService } from './preferencedefaults.service';
|
||||
import { SysPreferenceService } from './syspreferencedb.service';
|
||||
import { UsrPreferenceService } from './usrpreferencedb.service';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
TypeOrmModule.forFeature([ESysPreferenceBackend]),
|
||||
TypeOrmModule.forFeature([ESysPreferenceBackend, EUsrPreferenceBackend]),
|
||||
EarlyConfigModule,
|
||||
],
|
||||
providers: [
|
||||
SysPreferenceService,
|
||||
UsrPreferenceService,
|
||||
PreferenceDefaultsService,
|
||||
PreferenceCommonService,
|
||||
],
|
||||
exports: [SysPreferenceService],
|
||||
exports: [SysPreferenceService, UsrPreferenceService],
|
||||
})
|
||||
export class SysPreferenceModule {}
|
||||
export class PreferenceModule {}
|
||||
|
|
|
@ -43,7 +43,7 @@ export class UsrPreferenceService {
|
|||
try {
|
||||
// Upsert here, because we want to create a new record if it does not exist
|
||||
await this.usrPreferenceRepository.upsert(usrPreference, {
|
||||
conflictPaths: ['key', 'user'],
|
||||
conflictPaths: ['key', 'userId'],
|
||||
});
|
||||
} catch (e: any) {
|
||||
this.logger.warn(e);
|
||||
|
|
|
@ -5,7 +5,7 @@ import { generateRandomString } from 'picsur-shared/dist/util/random';
|
|||
import { AuthConfigService } from '../../config/early/auth.config.service';
|
||||
import { EarlyConfigModule } from '../../config/early/earlyconfig.module';
|
||||
import { EUserBackend } from '../../models/entities/user.entity';
|
||||
import { SysPreferenceModule } from '../preferencesdb/preferencedb.module';
|
||||
import { PreferenceModule } from '../preferencesdb/preferencedb.module';
|
||||
import { RolesModule } from '../roledb/roledb.module';
|
||||
import { UsersService } from './userdb.service';
|
||||
|
||||
|
@ -13,7 +13,7 @@ import { UsersService } from './userdb.service';
|
|||
imports: [
|
||||
EarlyConfigModule,
|
||||
RolesModule,
|
||||
SysPreferenceModule,
|
||||
PreferenceModule,
|
||||
TypeOrmModule.forFeature([EUserBackend]),
|
||||
],
|
||||
providers: [UsersService],
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { Logger, Module, OnModuleInit } from '@nestjs/common';
|
||||
import { SysPreferenceModule } from '../../collections/preferencesdb/preferencedb.module';
|
||||
import { PreferenceModule } from '../../collections/preferencesdb/preferencedb.module';
|
||||
import { SysPreferenceService } from '../../collections/preferencesdb/syspreferencedb.service';
|
||||
import { EarlyConfigModule } from '../early/earlyconfig.module';
|
||||
import { EarlyJwtConfigService } from '../early/earlyjwt.config.service';
|
||||
|
@ -11,7 +11,7 @@ import { JwtConfigService } from './jwt.config.service';
|
|||
// Otherwise we will create a circular depedency
|
||||
|
||||
@Module({
|
||||
imports: [SysPreferenceModule, EarlyConfigModule],
|
||||
imports: [PreferenceModule, EarlyConfigModule],
|
||||
providers: [JwtConfigService],
|
||||
exports: [JwtConfigService, EarlyConfigModule],
|
||||
})
|
||||
|
|
18
backend/src/decorators/request-user.decorator.ts
Normal file
18
backend/src/decorators/request-user.decorator.ts
Normal file
|
@ -0,0 +1,18 @@
|
|||
import { createParamDecorator, ExecutionContext } from '@nestjs/common';
|
||||
import AuthFasityRequest from '../models/requests/authrequest.dto';
|
||||
|
||||
export const ReqUser = createParamDecorator(
|
||||
(input: any, ctx: ExecutionContext) => {
|
||||
const request = ctx.switchToHttp().getRequest() as AuthFasityRequest;
|
||||
return request.user;
|
||||
},
|
||||
);
|
||||
|
||||
export const ReqUserID = createParamDecorator(
|
||||
(input: any, ctx: ExecutionContext) => {
|
||||
const request = ctx.switchToHttp().getRequest() as AuthFasityRequest;
|
||||
const id = request.user.id;
|
||||
if (!id) throw new Error('User ID is not set');
|
||||
return id;
|
||||
},
|
||||
);
|
|
@ -1,7 +1,7 @@
|
|||
import { Module } from '@nestjs/common';
|
||||
import { JwtModule } from '@nestjs/jwt';
|
||||
import { PassportModule } from '@nestjs/passport';
|
||||
import { SysPreferenceModule } from '../../collections/preferencesdb/preferencedb.module';
|
||||
import { PreferenceModule } from '../../collections/preferencesdb/preferencedb.module';
|
||||
import { UsersModule } from '../../collections/userdb/userdb.module';
|
||||
import { JwtConfigService, JwtSecretProvider } from '../../config/late/jwt.config.service';
|
||||
import { LateConfigModule } from '../../config/late/lateconfig.module';
|
||||
|
@ -15,7 +15,7 @@ import { GuestService } from './guest.service';
|
|||
imports: [
|
||||
UsersModule,
|
||||
PassportModule,
|
||||
SysPreferenceModule,
|
||||
PreferenceModule,
|
||||
LateConfigModule,
|
||||
JwtModule.registerAsync({
|
||||
useExisting: JwtConfigService,
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { IsEntityID } from 'picsur-shared/dist/validators/entity-id.validator';
|
||||
import { Column, Index, PrimaryGeneratedColumn, Unique } from 'typeorm';
|
||||
import { Column, Entity, Index, PrimaryGeneratedColumn, Unique } from 'typeorm';
|
||||
import z from 'zod';
|
||||
|
||||
export const EUsrPreferenceSchema = z.object({
|
||||
|
@ -10,6 +10,7 @@ export const EUsrPreferenceSchema = z.object({
|
|||
});
|
||||
type EUsrPreference = z.infer<typeof EUsrPreferenceSchema>;
|
||||
|
||||
@Entity()
|
||||
@Unique(['key', 'userId'])
|
||||
export class EUsrPreferenceBackend implements EUsrPreference {
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
|
|
|
@ -1,12 +1,26 @@
|
|||
import { Controller, Get, Request } from '@nestjs/common';
|
||||
import {
|
||||
Controller, Get,
|
||||
Request
|
||||
} from '@nestjs/common';
|
||||
import { UserInfoResponse } from 'picsur-shared/dist/dto/api/usermanage.dto';
|
||||
import { Permission } from 'picsur-shared/dist/dto/permissions.dto';
|
||||
import { RequiredPermissions } from '../../../decorators/permissions.decorator';
|
||||
import { ReqUserID } from '../../../decorators/request-user.decorator';
|
||||
import { Returns } from '../../../decorators/returns.decorator';
|
||||
import AuthFasityRequest from '../../../models/requests/authrequest.dto';
|
||||
|
||||
|
||||
|
||||
@Controller('api/experiment')
|
||||
@RequiredPermissions(Permission.Settings)
|
||||
export class ExperimentController {
|
||||
@Get()
|
||||
async testRoute(@Request() req: AuthFasityRequest) {
|
||||
return {
|
||||
message: req.user,
|
||||
};
|
||||
@Returns(UserInfoResponse)
|
||||
async testRoute(
|
||||
@Request() req: AuthFasityRequest,
|
||||
@ReqUserID() thing: string,
|
||||
): Promise<UserInfoResponse> {
|
||||
|
||||
return req.user;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
import { Module } from '@nestjs/common';
|
||||
import { SysPreferenceModule } from '../../../collections/preferencesdb/preferencedb.module';
|
||||
import { PrefController } from './pref.controller';
|
||||
import { PreferenceModule } from '../../../collections/preferencesdb/preferencedb.module';
|
||||
import { SysPrefController } from './syspref.controller';
|
||||
import { UsrPrefController } from './usrpref.controller';
|
||||
|
||||
@Module({
|
||||
imports: [SysPreferenceModule],
|
||||
controllers: [PrefController],
|
||||
imports: [PreferenceModule],
|
||||
controllers: [SysPrefController, UsrPrefController],
|
||||
})
|
||||
export class PrefModule {}
|
||||
|
|
|
@ -19,14 +19,14 @@ import { RequiredPermissions } from '../../../decorators/permissions.decorator';
|
|||
import { Returns } from '../../../decorators/returns.decorator';
|
||||
import { Permission } from '../../../models/dto/permissions.dto';
|
||||
|
||||
@Controller('api/pref')
|
||||
@Controller('api/pref/sys')
|
||||
@RequiredPermissions(Permission.SysPrefManage)
|
||||
export class PrefController {
|
||||
private readonly logger = new Logger('PrefController');
|
||||
export class SysPrefController {
|
||||
private readonly logger = new Logger('SysPrefController');
|
||||
|
||||
constructor(private prefService: SysPreferenceService) {}
|
||||
|
||||
@Get('sys')
|
||||
@Get()
|
||||
@Returns(MultipleSysPreferencesResponse)
|
||||
async getAllSysPrefs(): Promise<MultipleSysPreferencesResponse> {
|
||||
const prefs = await this.prefService.getAllPreferences();
|
||||
|
@ -41,7 +41,7 @@ export class PrefController {
|
|||
};
|
||||
}
|
||||
|
||||
@Get('sys/:key')
|
||||
@Get(':key')
|
||||
@Returns(GetSysPreferenceResponse)
|
||||
async getSysPref(
|
||||
@Param('key') key: string,
|
||||
|
@ -55,7 +55,7 @@ export class PrefController {
|
|||
return pref;
|
||||
}
|
||||
|
||||
@Post('sys/:key')
|
||||
@Post(':key')
|
||||
@Returns(UpdateSysPreferenceResponse)
|
||||
async setSysPref(
|
||||
@Param('key') key: string,
|
81
backend/src/routes/api/pref/usrpref.controller.ts
Normal file
81
backend/src/routes/api/pref/usrpref.controller.ts
Normal file
|
@ -0,0 +1,81 @@
|
|||
import {
|
||||
Body,
|
||||
Controller,
|
||||
Get,
|
||||
InternalServerErrorException,
|
||||
Logger,
|
||||
Param,
|
||||
Post
|
||||
} from '@nestjs/common';
|
||||
import {
|
||||
GetSysPreferenceResponse,
|
||||
MultipleSysPreferencesResponse,
|
||||
UpdateSysPreferenceRequest,
|
||||
UpdateSysPreferenceResponse
|
||||
} from 'picsur-shared/dist/dto/api/syspref.dto';
|
||||
import { HasFailed } from 'picsur-shared/dist/types';
|
||||
import { UsrPreferenceService } from '../../../collections/preferencesdb/usrpreferencedb.service';
|
||||
import { RequiredPermissions } from '../../../decorators/permissions.decorator';
|
||||
import { ReqUserID } from '../../../decorators/request-user.decorator';
|
||||
import { Returns } from '../../../decorators/returns.decorator';
|
||||
import { Permission } from '../../../models/dto/permissions.dto';
|
||||
|
||||
@Controller('api/pref/usr')
|
||||
@RequiredPermissions(Permission.Settings)
|
||||
export class UsrPrefController {
|
||||
private readonly logger = new Logger('UsrPrefController');
|
||||
|
||||
constructor(private prefService: UsrPreferenceService) {}
|
||||
|
||||
@Get()
|
||||
@Returns(MultipleSysPreferencesResponse)
|
||||
async getAllSysPrefs(@ReqUserID() userid: string): Promise<MultipleSysPreferencesResponse> {
|
||||
const prefs = await this.prefService.getAllPreferences(userid);
|
||||
if (HasFailed(prefs)) {
|
||||
this.logger.warn(prefs.getReason());
|
||||
throw new InternalServerErrorException('Could not get preferences');
|
||||
}
|
||||
|
||||
return {
|
||||
preferences: prefs,
|
||||
total: prefs.length,
|
||||
};
|
||||
}
|
||||
|
||||
@Get(':key')
|
||||
@Returns(GetSysPreferenceResponse)
|
||||
async getSysPref(
|
||||
@Param('key') key: string,
|
||||
@ReqUserID() userid: string
|
||||
): Promise<GetSysPreferenceResponse> {
|
||||
const pref = await this.prefService.getPreference(userid, key);
|
||||
if (HasFailed(pref)) {
|
||||
this.logger.warn(pref.getReason());
|
||||
throw new InternalServerErrorException('Could not get preference');
|
||||
}
|
||||
|
||||
return pref;
|
||||
}
|
||||
|
||||
@Post(':key')
|
||||
@Returns(UpdateSysPreferenceResponse)
|
||||
async setSysPref(
|
||||
@Param('key') key: string,
|
||||
@ReqUserID() userid: string,
|
||||
@Body() body: UpdateSysPreferenceRequest,
|
||||
): Promise<UpdateSysPreferenceResponse> {
|
||||
const value = body.value;
|
||||
|
||||
const pref = await this.prefService.setPreference(userid, key, value);
|
||||
if (HasFailed(pref)) {
|
||||
this.logger.warn(pref.getReason());
|
||||
throw new InternalServerErrorException('Could not set preference');
|
||||
}
|
||||
|
||||
return {
|
||||
key,
|
||||
value: pref.value,
|
||||
type: pref.type,
|
||||
};
|
||||
}
|
||||
}
|
|
@ -4,15 +4,16 @@ import {
|
|||
Get,
|
||||
InternalServerErrorException,
|
||||
Logger,
|
||||
Post,
|
||||
Request
|
||||
Post
|
||||
} from '@nestjs/common';
|
||||
import {
|
||||
UserLoginResponse, UserMePermissionsResponse,
|
||||
UserLoginResponse,
|
||||
UserMePermissionsResponse,
|
||||
UserMeResponse,
|
||||
UserRegisterRequest,
|
||||
UserRegisterResponse
|
||||
} from 'picsur-shared/dist/dto/api/user.dto';
|
||||
import { EUser } from 'picsur-shared/dist/entities/user.entity';
|
||||
import { HasFailed } from 'picsur-shared/dist/types';
|
||||
import { UsersService } from '../../../collections/userdb/userdb.service';
|
||||
import {
|
||||
|
@ -20,10 +21,10 @@ import {
|
|||
RequiredPermissions,
|
||||
UseLocalAuth
|
||||
} from '../../../decorators/permissions.decorator';
|
||||
import { ReqUser, ReqUserID } from '../../../decorators/request-user.decorator';
|
||||
import { Returns } from '../../../decorators/returns.decorator';
|
||||
import { AuthManagerService } from '../../../managers/auth/auth.service';
|
||||
import { Permission } from '../../../models/dto/permissions.dto';
|
||||
import AuthFasityRequest from '../../../models/requests/authrequest.dto';
|
||||
import { EUserBackend2EUser } from '../../../models/transformers/user.transformer';
|
||||
|
||||
@Controller('api/user')
|
||||
|
@ -38,8 +39,8 @@ export class UserController {
|
|||
@Post('login')
|
||||
@Returns(UserLoginResponse)
|
||||
@UseLocalAuth(Permission.UserLogin)
|
||||
async login(@Request() req: AuthFasityRequest): Promise<UserLoginResponse> {
|
||||
const jwt_token = await this.authService.createToken(req.user);
|
||||
async login(@ReqUser() user: EUser): Promise<UserLoginResponse> {
|
||||
const jwt_token = await this.authService.createToken(user);
|
||||
if (HasFailed(jwt_token)) {
|
||||
this.logger.warn(jwt_token.getReason());
|
||||
throw new InternalServerErrorException('Could not get new token');
|
||||
|
@ -69,10 +70,8 @@ export class UserController {
|
|||
@Get('me')
|
||||
@Returns(UserMeResponse)
|
||||
@RequiredPermissions(Permission.UserKeepLogin)
|
||||
async me(@Request() req: AuthFasityRequest): Promise<UserMeResponse> {
|
||||
if (!req.user.id) throw new InternalServerErrorException('User is corrupt');
|
||||
|
||||
const backenduser = await this.usersService.findOne(req.user.id);
|
||||
async me(@ReqUserID() userid: string): Promise<UserMeResponse> {
|
||||
const backenduser = await this.usersService.findOne(userid);
|
||||
|
||||
if (HasFailed(backenduser)) {
|
||||
this.logger.warn(backenduser.getReason());
|
||||
|
@ -95,11 +94,9 @@ export class UserController {
|
|||
@Returns(UserMePermissionsResponse)
|
||||
@NoPermissions()
|
||||
async refresh(
|
||||
@Request() req: AuthFasityRequest,
|
||||
@ReqUserID() userid: string,
|
||||
): Promise<UserMePermissionsResponse> {
|
||||
if (!req.user.id) throw new InternalServerErrorException('User is corrupt');
|
||||
|
||||
const permissions = await this.usersService.getPermissions(req.user.id);
|
||||
const permissions = await this.usersService.getPermissions(userid);
|
||||
if (HasFailed(permissions)) {
|
||||
this.logger.warn(permissions.getReason());
|
||||
throw new InternalServerErrorException('Could not get permissions');
|
||||
|
|
Loading…
Reference in a new issue