add user preferences endpoint

This commit is contained in:
rubikscraft 2022-04-13 17:28:48 +02:00
parent 2bbe798097
commit 5632eaffdf
No known key found for this signature in database
GPG key ID: 1463EBE9200A5CD4
12 changed files with 155 additions and 40 deletions

View file

@ -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 {}

View file

@ -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);

View file

@ -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],

View file

@ -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],
})

View 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;
},
);

View file

@ -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,

View file

@ -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')

View file

@ -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;
}
}

View file

@ -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 {}

View file

@ -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,

View 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,
};
}
}

View file

@ -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');