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