add user role api
This commit is contained in:
parent
1febcd8147
commit
6d2e950230
|
@ -36,7 +36,7 @@ export class RolesModule implements OnModuleInit {
|
|||
|
||||
private async nukeRoles() {
|
||||
this.logger.error('Nuking system roles');
|
||||
const result = this.rolesService.nukeSystemRoles(true);
|
||||
const result = await this.rolesService.nukeSystemRoles(true);
|
||||
if (HasFailed(result)) {
|
||||
this.logger.error(`Failed to nuke roles because: ${result.getReason()}`);
|
||||
}
|
||||
|
|
|
@ -132,7 +132,7 @@ export class RolesService {
|
|||
where: { name },
|
||||
});
|
||||
|
||||
if (!found) return Fail('User not found');
|
||||
if (!found) return Fail('Role not found');
|
||||
return found as ERoleBackend;
|
||||
} catch (e: any) {
|
||||
return Fail(e?.message);
|
||||
|
|
|
@ -88,43 +88,46 @@ export class UsersService {
|
|||
public async addRoles(
|
||||
user: string | EUserBackend,
|
||||
roles: Roles,
|
||||
): AsyncFailable<true> {
|
||||
): AsyncFailable<EUserBackend> {
|
||||
const userToModify = await this.resolve(user);
|
||||
if (HasFailed(userToModify)) return userToModify;
|
||||
|
||||
// This is stupid
|
||||
userToModify.roles = [...new Set([...userToModify.roles, ...roles])];
|
||||
const newRoles = [...new Set([...userToModify.roles, ...roles])];
|
||||
|
||||
try {
|
||||
await this.usersRepository.save(userToModify);
|
||||
} catch (e: any) {
|
||||
return Fail(e?.message);
|
||||
}
|
||||
|
||||
return true;
|
||||
return this.setRoles(userToModify, newRoles);
|
||||
}
|
||||
|
||||
public async removeRoles(
|
||||
user: string | EUserBackend,
|
||||
roles: Roles,
|
||||
): AsyncFailable<true> {
|
||||
): AsyncFailable<EUserBackend> {
|
||||
const userToModify = await this.resolve(user);
|
||||
if (HasFailed(userToModify)) return userToModify;
|
||||
|
||||
// Make sure we don't remove unremovable roles
|
||||
roles = roles.filter((role) => !PermanentRolesList.includes(role));
|
||||
const newRoles = userToModify.roles.filter((role) => !roles.includes(role));
|
||||
|
||||
userToModify.roles = userToModify.roles.filter(
|
||||
(role) => !roles.includes(role),
|
||||
return this.setRoles(userToModify, newRoles);
|
||||
}
|
||||
|
||||
public async setRoles(
|
||||
user: string | EUserBackend,
|
||||
roles: Roles,
|
||||
): AsyncFailable<EUserBackend> {
|
||||
const userToModify = await this.resolve(user);
|
||||
if (HasFailed(userToModify)) return userToModify;
|
||||
|
||||
const rolesToKeep = userToModify.roles.filter((role) =>
|
||||
PermanentRolesList.includes(role),
|
||||
);
|
||||
const newRoles = [...new Set([...rolesToKeep, ...roles])];
|
||||
|
||||
userToModify.roles = newRoles;
|
||||
|
||||
try {
|
||||
await this.usersRepository.save(userToModify);
|
||||
return await this.usersRepository.save(userToModify);
|
||||
} catch (e: any) {
|
||||
return Fail(e?.message);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public async findOne<B extends true | undefined = undefined>(
|
||||
|
|
|
@ -2,7 +2,7 @@ import {
|
|||
ArgumentsHost, Catch, ExceptionFilter, HttpException
|
||||
} from '@nestjs/common';
|
||||
import { FastifyReply, FastifyRequest } from 'fastify';
|
||||
import { ApiErrorResponse } from 'picsur-shared/dist/dto/api.dto';
|
||||
import { ApiErrorResponse } from 'picsur-shared/dist/dto/api';
|
||||
|
||||
@Catch(HttpException)
|
||||
export class MainExceptionFilter implements ExceptionFilter {
|
||||
|
|
|
@ -2,7 +2,7 @@ import {
|
|||
CallHandler, ExecutionContext, Injectable,
|
||||
NestInterceptor
|
||||
} from '@nestjs/common';
|
||||
import { ApiResponse } from 'picsur-shared/dist/dto/api.dto';
|
||||
import { ApiResponse } from 'picsur-shared/dist/dto/api';
|
||||
import { map, Observable } from 'rxjs';
|
||||
|
||||
@Injectable()
|
||||
|
|
|
@ -2,7 +2,7 @@ import { Injectable, Logger } from '@nestjs/common';
|
|||
import { JwtService } from '@nestjs/jwt';
|
||||
import { instanceToPlain, plainToClass } from 'class-transformer';
|
||||
import { validate } from 'class-validator';
|
||||
import { JwtDataDto } from 'picsur-shared/dist/dto/auth.dto';
|
||||
import { JwtDataDto } from 'picsur-shared/dist/dto/jwt.dto';
|
||||
import { EUserBackend } from '../../models/entities/user.entity';
|
||||
|
||||
@Injectable()
|
||||
|
|
|
@ -8,8 +8,9 @@ import { PassportStrategy } from '@nestjs/passport';
|
|||
import { plainToClass } from 'class-transformer';
|
||||
import { validate } from 'class-validator';
|
||||
import { ExtractJwt, Strategy } from 'passport-jwt';
|
||||
import { JwtDataDto } from 'picsur-shared/dist/dto/auth.dto';
|
||||
import { JwtDataDto } from 'picsur-shared/dist/dto/jwt.dto';
|
||||
import { EUserBackend } from '../../../models/entities/user.entity';
|
||||
|
||||
@Injectable()
|
||||
export class JwtStrategy extends PassportStrategy(Strategy, 'jwt') {
|
||||
private readonly logger = new Logger('JwtStrategy');
|
||||
|
|
|
@ -42,13 +42,13 @@ export class MainAuthGuard extends AuthGuard(['jwt', 'guest']) {
|
|||
|
||||
const permissions = this.extractPermissions(context);
|
||||
if (HasFailed(permissions)) {
|
||||
this.logger.warn(permissions.getReason());
|
||||
this.logger.warn("222"+permissions.getReason());
|
||||
throw new InternalServerErrorException();
|
||||
}
|
||||
|
||||
const userPermissions = await this.usersService.getPermissions(user);
|
||||
if (HasFailed(userPermissions)) {
|
||||
this.logger.warn(userPermissions.getReason());
|
||||
this.logger.warn("111"+userPermissions.getReason());
|
||||
throw new InternalServerErrorException();
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { Module } from '@nestjs/common';
|
||||
import { AuthModule } from './auth/auth.module';
|
||||
import { UserApiModule } from './auth/user.module';
|
||||
import { ExperimentModule } from './experiment/experiment.module';
|
||||
import { InfoModule } from './info/info.module';
|
||||
import { PrefModule } from './pref/pref.module';
|
||||
|
@ -7,7 +7,7 @@ import { RolesApiModule } from './roles/roles.module';
|
|||
|
||||
@Module({
|
||||
imports: [
|
||||
AuthModule,
|
||||
UserApiModule,
|
||||
PrefModule,
|
||||
ExperimentModule,
|
||||
InfoModule,
|
||||
|
|
|
@ -7,13 +7,19 @@ import {
|
|||
Post,
|
||||
Request
|
||||
} from '@nestjs/common';
|
||||
import { AuthUserInfoRequest } from 'picsur-shared/dist/dto/api/auth.dto';
|
||||
import {
|
||||
AuthDeleteRequest,
|
||||
AuthLoginResponse,
|
||||
AuthMeResponse,
|
||||
AuthRegisterRequest
|
||||
} from 'picsur-shared/dist/dto/auth.dto';
|
||||
UserDeleteRequest,
|
||||
UserDeleteResponse,
|
||||
UserInfoRequest,
|
||||
UserInfoResponse,
|
||||
UserListResponse,
|
||||
UserLoginResponse,
|
||||
UserMeResponse,
|
||||
UserRegisterRequest,
|
||||
UserRegisterResponse,
|
||||
UserUpdateRolesRequest,
|
||||
UserUpdateRolesResponse
|
||||
} from 'picsur-shared/dist/dto/api/user.dto';
|
||||
import { HasFailed } from 'picsur-shared/dist/types';
|
||||
import { UsersService } from '../../../collections/userdb/userdb.service';
|
||||
import {
|
||||
|
@ -23,8 +29,8 @@ import {
|
|||
import { AuthManagerService } from '../../../managers/auth/auth.service';
|
||||
import AuthFasityRequest from '../../../models/dto/authrequest.dto';
|
||||
|
||||
@Controller('api/auth')
|
||||
export class AuthController {
|
||||
@Controller('api/user')
|
||||
export class UserController {
|
||||
private readonly logger = new Logger('AuthController');
|
||||
|
||||
constructor(
|
||||
|
@ -34,20 +40,17 @@ export class AuthController {
|
|||
|
||||
@Post('login')
|
||||
@UseLocalAuth('user-login')
|
||||
async login(@Request() req: AuthFasityRequest) {
|
||||
const response: AuthLoginResponse = {
|
||||
async login(@Request() req: AuthFasityRequest): Promise<UserLoginResponse> {
|
||||
return {
|
||||
jwt_token: await this.authService.createToken(req.user),
|
||||
};
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
@Post('register')
|
||||
@RequiredPermissions('user-register')
|
||||
async register(
|
||||
@Request() req: AuthFasityRequest,
|
||||
@Body() register: AuthRegisterRequest,
|
||||
) {
|
||||
@Body() register: UserRegisterRequest,
|
||||
): Promise<UserRegisterResponse> {
|
||||
const user = await this.usersService.create(
|
||||
register.username,
|
||||
register.password,
|
||||
|
@ -58,7 +61,11 @@ export class AuthController {
|
|||
}
|
||||
|
||||
if (register.isAdmin) {
|
||||
await this.usersService.addRoles(user, ['admin']);
|
||||
const result = await this.usersService.addRoles(user, ['admin']);
|
||||
if (HasFailed(result)) {
|
||||
this.logger.warn(result.getReason());
|
||||
throw new InternalServerErrorException('Could not add admin role');
|
||||
}
|
||||
}
|
||||
|
||||
return user;
|
||||
|
@ -67,9 +74,8 @@ export class AuthController {
|
|||
@Post('delete')
|
||||
@RequiredPermissions('user-manage')
|
||||
async delete(
|
||||
@Request() req: AuthFasityRequest,
|
||||
@Body() deleteData: AuthDeleteRequest,
|
||||
) {
|
||||
@Body() deleteData: UserDeleteRequest,
|
||||
): Promise<UserDeleteResponse> {
|
||||
const user = await this.usersService.delete(deleteData.username);
|
||||
if (HasFailed(user)) {
|
||||
this.logger.warn(user.getReason());
|
||||
|
@ -79,9 +85,27 @@ export class AuthController {
|
|||
return user;
|
||||
}
|
||||
|
||||
@Post('roles')
|
||||
@RequiredPermissions('user-manage')
|
||||
async setPermissions(
|
||||
@Body() body: UserUpdateRolesRequest,
|
||||
): Promise<UserUpdateRolesResponse> {
|
||||
const updatedUser = await this.usersService.setRoles(
|
||||
body.username,
|
||||
body.roles,
|
||||
);
|
||||
|
||||
if (HasFailed(updatedUser)) {
|
||||
this.logger.warn(updatedUser.getReason());
|
||||
throw new InternalServerErrorException('Could not update user');
|
||||
}
|
||||
|
||||
return updatedUser;
|
||||
}
|
||||
|
||||
@Post('info')
|
||||
@RequiredPermissions('user-manage')
|
||||
async getUser(@Body() body: AuthUserInfoRequest) {
|
||||
async getUser(@Body() body: UserInfoRequest): Promise<UserInfoResponse> {
|
||||
const user = await this.usersService.findOne(body.username);
|
||||
if (HasFailed(user)) {
|
||||
this.logger.warn(user.getReason());
|
||||
|
@ -93,30 +117,32 @@ export class AuthController {
|
|||
|
||||
@Get('list')
|
||||
@RequiredPermissions('user-manage')
|
||||
async listUsers(@Request() req: AuthFasityRequest) {
|
||||
const users = this.usersService.findAll();
|
||||
async listUsers(): Promise<UserListResponse> {
|
||||
const users = await this.usersService.findAll();
|
||||
if (HasFailed(users)) {
|
||||
this.logger.warn(users.getReason());
|
||||
throw new InternalServerErrorException('Could not list users');
|
||||
}
|
||||
|
||||
return users;
|
||||
return {
|
||||
users,
|
||||
total: users.length,
|
||||
};
|
||||
}
|
||||
|
||||
@Get('me')
|
||||
@RequiredPermissions('user-view')
|
||||
async me(@Request() req: AuthFasityRequest) {
|
||||
async me(@Request() req: AuthFasityRequest): Promise<UserMeResponse> {
|
||||
const permissions = await this.usersService.getPermissions(req.user);
|
||||
if (HasFailed(permissions)) {
|
||||
this.logger.warn(permissions.getReason());
|
||||
throw new InternalServerErrorException('Could not get permissions');
|
||||
}
|
||||
|
||||
const meResponse: AuthMeResponse = new AuthMeResponse();
|
||||
meResponse.user = req.user;
|
||||
meResponse.permissions = permissions;
|
||||
meResponse.newJwtToken = await this.authService.createToken(req.user);
|
||||
|
||||
return meResponse;
|
||||
return {
|
||||
user: req.user,
|
||||
permissions,
|
||||
newJwtToken: await this.authService.createToken(req.user),
|
||||
};
|
||||
}
|
||||
}
|
|
@ -1,9 +1,9 @@
|
|||
import { Module } from '@nestjs/common';
|
||||
import { AuthManagerModule } from '../../../managers/auth/auth.module';
|
||||
import { AuthController } from './auth.controller';
|
||||
import { UserController } from './user.controller';
|
||||
|
||||
@Module({
|
||||
imports: [AuthManagerModule],
|
||||
controllers: [AuthController],
|
||||
controllers: [UserController],
|
||||
})
|
||||
export class AuthModule {}
|
||||
export class UserApiModule {}
|
|
@ -9,11 +9,10 @@ export class InfoController {
|
|||
|
||||
@Get()
|
||||
@NoAuth()
|
||||
getInfo() {
|
||||
const response: InfoResponse = {
|
||||
async getInfo(): Promise<InfoResponse> {
|
||||
return {
|
||||
demo: this.hostConfig.isDemo(),
|
||||
production: this.hostConfig.isProduction(),
|
||||
};
|
||||
return response;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,9 +8,10 @@ import {
|
|||
Post
|
||||
} from '@nestjs/common';
|
||||
import {
|
||||
SysPreferences,
|
||||
SysPreferenceResponse,
|
||||
UpdateSysPreferenceRequest
|
||||
} from 'picsur-shared/dist/dto/syspreferences.dto';
|
||||
} from 'picsur-shared/dist/dto/api/pref.dto';
|
||||
import { SysPreferences } from 'picsur-shared/dist/dto/syspreferences.dto';
|
||||
import { HasFailed } from 'picsur-shared/dist/types';
|
||||
import { SysPreferenceService } from '../../../collections/syspreferencesdb/syspreferencedb.service';
|
||||
import { RequiredPermissions } from '../../../decorators/permissions.decorator';
|
||||
|
@ -23,7 +24,7 @@ export class PrefController {
|
|||
constructor(private prefService: SysPreferenceService) {}
|
||||
|
||||
@Get('sys/:key')
|
||||
async getSysPref(@Param('key') key: string) {
|
||||
async getSysPref(@Param('key') key: string): Promise<SysPreferenceResponse> {
|
||||
const returned = await this.prefService.getPreference(
|
||||
key as SysPreferences,
|
||||
);
|
||||
|
@ -39,7 +40,7 @@ export class PrefController {
|
|||
async setSysPref(
|
||||
@Param('key') key: string,
|
||||
@Body() body: UpdateSysPreferenceRequest,
|
||||
) {
|
||||
): Promise<SysPreferenceResponse> {
|
||||
const value = body.value;
|
||||
const returned = await this.prefService.setPreference(
|
||||
key as SysPreferences,
|
||||
|
|
|
@ -8,9 +8,14 @@ import {
|
|||
} from '@nestjs/common';
|
||||
import {
|
||||
RoleCreateRequest,
|
||||
RoleCreateResponse,
|
||||
RoleDeleteRequest,
|
||||
RoleDeleteResponse,
|
||||
RoleInfoRequest,
|
||||
RoleUpdateRequest
|
||||
RoleInfoResponse,
|
||||
RoleListResponse,
|
||||
RoleUpdateRequest,
|
||||
RoleUpdateResponse
|
||||
} from 'picsur-shared/dist/dto/api/roles.dto';
|
||||
import { HasFailed } from 'picsur-shared/dist/types';
|
||||
import { RolesService } from '../../../collections/roledb/roledb.service';
|
||||
|
@ -24,19 +29,22 @@ export class RolesController {
|
|||
constructor(private rolesService: RolesService) {}
|
||||
|
||||
@Get('/list')
|
||||
getRoles() {
|
||||
const roles = this.rolesService.findAll();
|
||||
async getRoles(): Promise<RoleListResponse> {
|
||||
const roles = await this.rolesService.findAll();
|
||||
if (HasFailed(roles)) {
|
||||
this.logger.warn(roles.getReason());
|
||||
throw new InternalServerErrorException('Could not list roles');
|
||||
}
|
||||
|
||||
return roles;
|
||||
return {
|
||||
roles,
|
||||
total: roles.length,
|
||||
};
|
||||
}
|
||||
|
||||
@Post('/info')
|
||||
getRole(@Body() body: RoleInfoRequest) {
|
||||
const role = this.rolesService.findOne(body.name);
|
||||
async getRole(@Body() body: RoleInfoRequest): Promise<RoleInfoResponse> {
|
||||
const role = await this.rolesService.findOne(body.name);
|
||||
if (HasFailed(role)) {
|
||||
this.logger.warn(role.getReason());
|
||||
throw new InternalServerErrorException('Could not find role');
|
||||
|
@ -45,9 +53,11 @@ export class RolesController {
|
|||
return role;
|
||||
}
|
||||
|
||||
@Post('/permissions/set')
|
||||
updateRole(@Body() body: RoleUpdateRequest) {
|
||||
const updatedRole = this.rolesService.setPermissions(
|
||||
@Post('/permissions')
|
||||
async updateRole(
|
||||
@Body() body: RoleUpdateRequest,
|
||||
): Promise<RoleUpdateResponse> {
|
||||
const updatedRole = await this.rolesService.setPermissions(
|
||||
body.name,
|
||||
body.permissions,
|
||||
);
|
||||
|
@ -59,39 +69,11 @@ export class RolesController {
|
|||
return updatedRole;
|
||||
}
|
||||
|
||||
@Post('/permissions/add')
|
||||
addPermissions(@Body() body: RoleUpdateRequest) {
|
||||
const updatedRole = this.rolesService.addPermissions(
|
||||
body.name,
|
||||
body.permissions,
|
||||
);
|
||||
if (HasFailed(updatedRole)) {
|
||||
this.logger.warn(updatedRole.getReason());
|
||||
throw new InternalServerErrorException('Could not add role permissions');
|
||||
}
|
||||
|
||||
return updatedRole;
|
||||
}
|
||||
|
||||
@Post('/permissions/remove')
|
||||
removePermissions(@Body() body: RoleUpdateRequest) {
|
||||
const updatedRole = this.rolesService.removePermissions(
|
||||
body.name,
|
||||
body.permissions,
|
||||
);
|
||||
if (HasFailed(updatedRole)) {
|
||||
this.logger.warn(updatedRole.getReason());
|
||||
throw new InternalServerErrorException(
|
||||
'Could not remove role permissions',
|
||||
);
|
||||
}
|
||||
|
||||
return updatedRole;
|
||||
}
|
||||
|
||||
@Post('/create')
|
||||
createRole(@Body() role: RoleCreateRequest) {
|
||||
const newRole = this.rolesService.create(role.name, role.permissions);
|
||||
async createRole(
|
||||
@Body() role: RoleCreateRequest,
|
||||
): Promise<RoleCreateResponse> {
|
||||
const newRole = await this.rolesService.create(role.name, role.permissions);
|
||||
if (HasFailed(newRole)) {
|
||||
this.logger.warn(newRole.getReason());
|
||||
throw new InternalServerErrorException('Could not create role');
|
||||
|
@ -101,8 +83,10 @@ export class RolesController {
|
|||
}
|
||||
|
||||
@Post('/delete')
|
||||
deleteRole(@Body() role: RoleDeleteRequest) {
|
||||
const deletedRole = this.rolesService.delete(role.name);
|
||||
async deleteRole(
|
||||
@Body() role: RoleDeleteRequest,
|
||||
): Promise<RoleDeleteResponse> {
|
||||
const deletedRole = await this.rolesService.delete(role.name);
|
||||
if (HasFailed(deletedRole)) {
|
||||
this.logger.warn(deletedRole.getReason());
|
||||
throw new InternalServerErrorException('Could not delete role');
|
||||
|
|
|
@ -12,6 +12,7 @@ import {
|
|||
} from '@nestjs/common';
|
||||
import { isHash } from 'class-validator';
|
||||
import { FastifyReply, FastifyRequest } from 'fastify';
|
||||
import { ImageMetaResponse } from 'picsur-shared/dist/dto/api/image.dto';
|
||||
import { HasFailed } from 'picsur-shared/dist/types';
|
||||
import { MultiPart } from '../../decorators/multipart.decorator';
|
||||
import { RequiredPermissions } from '../../decorators/permissions.decorator';
|
||||
|
@ -29,7 +30,7 @@ export class ImageController {
|
|||
async getImage(
|
||||
@Res({ passthrough: true }) res: FastifyReply,
|
||||
@Param('hash') hash: string,
|
||||
) {
|
||||
): Promise<Buffer> {
|
||||
if (!isHash(hash, 'sha256')) throw new BadRequestException('Invalid hash');
|
||||
|
||||
const image = await this.imagesService.retrieveComplete(hash);
|
||||
|
@ -43,7 +44,7 @@ export class ImageController {
|
|||
}
|
||||
|
||||
@Get('meta/:hash')
|
||||
async getImageMeta(@Param('hash') hash: string) {
|
||||
async getImageMeta(@Param('hash') hash: string): Promise<ImageMetaResponse> {
|
||||
if (!isHash(hash, 'sha256')) throw new BadRequestException('Invalid hash');
|
||||
|
||||
const image = await this.imagesService.retrieveInfo(hash);
|
||||
|
@ -60,7 +61,7 @@ export class ImageController {
|
|||
async uploadImage(
|
||||
@Req() req: FastifyRequest,
|
||||
@MultiPart(ImageUploadDto) multipart: ImageUploadDto,
|
||||
) {
|
||||
): Promise<ImageMetaResponse> {
|
||||
const fileBuffer = await multipart.image.toBuffer();
|
||||
const image = await this.imagesService.upload(fileBuffer);
|
||||
if (HasFailed(image)) {
|
||||
|
|
|
@ -4,12 +4,11 @@ import { validate } from 'class-validator';
|
|||
import {
|
||||
ApiResponse,
|
||||
ApiSuccessResponse
|
||||
} from 'picsur-shared/dist/dto/api.dto';
|
||||
} from 'picsur-shared/dist/dto/api';
|
||||
import { AsyncFailable, Fail, HasFailed } from 'picsur-shared/dist/types';
|
||||
import { MultiPartRequest } from '../models/multi-part-request';
|
||||
import { KeyService } from './key.service';
|
||||
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
import { ImageMetaResponse } from 'picsur-shared/dist/dto/api/image.dto';
|
||||
import { EImage } from 'picsur-shared/dist/entities/image.entity';
|
||||
import { AsyncFailable, HasFailed } from 'picsur-shared/dist/types';
|
||||
import { ImageUploadRequest } from '../models/image-upload-request';
|
||||
|
@ -12,7 +13,7 @@ export class ImageService {
|
|||
|
||||
public async UploadImage(image: File): AsyncFailable<string> {
|
||||
const result = await this.api.postForm(
|
||||
EImage,
|
||||
ImageMetaResponse,
|
||||
'/i',
|
||||
new ImageUploadRequest(image)
|
||||
);
|
||||
|
@ -23,7 +24,7 @@ export class ImageService {
|
|||
}
|
||||
|
||||
public async GetImageMeta(image: string): AsyncFailable<EImage> {
|
||||
return await this.api.get(EImage, `/i/meta/${image}`);
|
||||
return await this.api.get(ImageMetaResponse, `/i/meta/${image}`);
|
||||
}
|
||||
|
||||
public GetImageURL(image: string): string {
|
||||
|
|
|
@ -3,16 +3,13 @@ import { plainToClass } from 'class-transformer';
|
|||
import { validate } from 'class-validator';
|
||||
import jwt_decode from 'jwt-decode';
|
||||
import {
|
||||
AuthLoginRequest,
|
||||
AuthLoginResponse,
|
||||
AuthMeResponse,
|
||||
JwtDataDto
|
||||
} from 'picsur-shared/dist/dto/auth.dto';
|
||||
UserLoginRequest,
|
||||
UserLoginResponse,
|
||||
UserMeResponse
|
||||
} from 'picsur-shared/dist/dto/api/user.dto';
|
||||
import { JwtDataDto } from 'picsur-shared/dist/dto/jwt.dto';
|
||||
import { EUser } from 'picsur-shared/dist/entities/user.entity';
|
||||
import {
|
||||
AsyncFailable,
|
||||
Fail, HasFailed
|
||||
} from 'picsur-shared/dist/types';
|
||||
import { AsyncFailable, Fail, HasFailed } from 'picsur-shared/dist/types';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
import { ApiService } from './api.service';
|
||||
import { KeyService } from './key.service';
|
||||
|
@ -38,19 +35,19 @@ export class UserService {
|
|||
private userSubject = new BehaviorSubject<EUser | null>(null);
|
||||
|
||||
constructor(private api: ApiService, private key: KeyService) {
|
||||
this.init().catch(console.error);
|
||||
this.init().catch(this.logger.error);
|
||||
}
|
||||
|
||||
public async login(username: string, password: string): AsyncFailable<EUser> {
|
||||
const request: AuthLoginRequest = {
|
||||
const request: UserLoginRequest = {
|
||||
username,
|
||||
password,
|
||||
};
|
||||
|
||||
const response = await this.api.post(
|
||||
AuthLoginRequest,
|
||||
AuthLoginResponse,
|
||||
'/api/auth/login',
|
||||
UserLoginRequest,
|
||||
UserLoginResponse,
|
||||
'/api/user/login',
|
||||
request
|
||||
);
|
||||
|
||||
|
@ -117,7 +114,7 @@ export class UserService {
|
|||
}
|
||||
|
||||
private async fetchUser(): AsyncFailable<EUser> {
|
||||
const got = await this.api.get(AuthMeResponse, '/api/auth/me');
|
||||
const got = await this.api.get(UserMeResponse, '/api/user/me');
|
||||
if (HasFailed(got)) return got;
|
||||
|
||||
this.key.set(got.newJwtToken);
|
||||
|
|
|
@ -13,6 +13,8 @@ import { UtilService } from 'src/app/util/util.service';
|
|||
styleUrls: ['./header.component.scss'],
|
||||
})
|
||||
export class HeaderComponent implements OnInit {
|
||||
private readonly logger = console;
|
||||
|
||||
private currentUser: EUser | null = null;
|
||||
|
||||
public get user() {
|
||||
|
@ -36,7 +38,7 @@ export class HeaderComponent implements OnInit {
|
|||
@AutoUnsubscribe()
|
||||
subscribeUser() {
|
||||
return this.userService.liveUser.subscribe((user) => {
|
||||
console.log('user', user);
|
||||
this.logger.log('user', user);
|
||||
this.currentUser = user;
|
||||
});
|
||||
}
|
||||
|
|
3
shared/src/dto/api/image.dto.ts
Normal file
3
shared/src/dto/api/image.dto.ts
Normal file
|
@ -0,0 +1,3 @@
|
|||
import { EImage } from '../../entities/image.entity';
|
||||
|
||||
export class ImageMetaResponse extends EImage {}
|
9
shared/src/dto/api/pref.dto.ts
Normal file
9
shared/src/dto/api/pref.dto.ts
Normal file
|
@ -0,0 +1,9 @@
|
|||
import { IsNotEmpty } from 'class-validator';
|
||||
import { ESysPreference } from '../../entities/syspreference.entity';
|
||||
|
||||
export class UpdateSysPreferenceRequest {
|
||||
@IsNotEmpty()
|
||||
value: string;
|
||||
}
|
||||
|
||||
export class SysPreferenceResponse extends ESysPreference {}
|
|
@ -1,13 +1,32 @@
|
|||
import { IsArray, IsEnum, IsNotEmpty, IsString } from 'class-validator';
|
||||
import { Type } from 'class-transformer';
|
||||
import { IsArray, IsDefined, IsEnum, IsInt, IsNotEmpty, IsPositive, IsString, ValidateNested } from 'class-validator';
|
||||
import { ERole } from '../../entities/role.entity';
|
||||
import { Permissions, PermissionsList } from '../permissions';
|
||||
|
||||
// RoleInfo
|
||||
export class RoleInfoRequest {
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
name: string;
|
||||
}
|
||||
|
||||
export class RoleInfoResponse extends ERole {}
|
||||
|
||||
// RoleList
|
||||
export class RoleListResponse {
|
||||
@IsArray()
|
||||
@IsDefined()
|
||||
@ValidateNested()
|
||||
@Type(() => ERole)
|
||||
roles: ERole[];
|
||||
|
||||
@IsInt()
|
||||
@IsPositive()
|
||||
@IsDefined()
|
||||
total: number;
|
||||
}
|
||||
|
||||
// RoleUpdate
|
||||
export class RoleUpdateRequest {
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
|
@ -17,10 +36,15 @@ export class RoleUpdateRequest {
|
|||
@IsEnum(PermissionsList, { each: true })
|
||||
permissions: Permissions;
|
||||
}
|
||||
export class RoleUpdateResponse extends ERole {}
|
||||
|
||||
// RoleCreate
|
||||
export class RoleCreateRequest extends ERole {}
|
||||
export class RoleCreateResponse extends ERole {}
|
||||
|
||||
// RoleDelete
|
||||
export class RoleDeleteRequest {
|
||||
@IsNotEmpty()
|
||||
name: string;
|
||||
}
|
||||
export class RoleDeleteResponse extends ERole {}
|
||||
|
|
|
@ -3,17 +3,22 @@ import {
|
|||
IsArray,
|
||||
IsBoolean,
|
||||
IsDefined,
|
||||
IsEnum, IsNotEmpty,
|
||||
IsEnum,
|
||||
IsInt,
|
||||
IsNotEmpty,
|
||||
IsOptional,
|
||||
IsPositive,
|
||||
IsString,
|
||||
ValidateNested
|
||||
} from 'class-validator';
|
||||
import { EUser } from '../../entities/user.entity';
|
||||
import { Permissions, PermissionsList } from '../permissions';
|
||||
import { Roles } from '../roles.dto';
|
||||
|
||||
// Api
|
||||
|
||||
export class AuthLoginRequest {
|
||||
// UserLogin
|
||||
export class UserLoginRequest {
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
username: string;
|
||||
|
@ -23,13 +28,14 @@ export class AuthLoginRequest {
|
|||
password: string;
|
||||
}
|
||||
|
||||
export class AuthLoginResponse {
|
||||
export class UserLoginResponse {
|
||||
@IsString()
|
||||
@IsDefined()
|
||||
jwt_token: string;
|
||||
}
|
||||
|
||||
export class AuthRegisterRequest {
|
||||
// UserRegister
|
||||
export class UserRegisterRequest {
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
username: string;
|
||||
|
@ -43,15 +49,42 @@ export class AuthRegisterRequest {
|
|||
isAdmin?: boolean;
|
||||
}
|
||||
|
||||
export class AuthDeleteRequest {
|
||||
export class UserRegisterResponse extends EUser {}
|
||||
|
||||
// UserDelete
|
||||
export class UserDeleteRequest {
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
username: string;
|
||||
}
|
||||
|
||||
export class AuthDeleteResponse extends EUser {}
|
||||
export class UserDeleteResponse extends EUser {}
|
||||
|
||||
export class AuthMeResponse {
|
||||
// UserInfo
|
||||
export class UserInfoRequest {
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
username: string;
|
||||
}
|
||||
|
||||
export class UserInfoResponse extends EUser {}
|
||||
|
||||
// UserList
|
||||
export class UserListResponse {
|
||||
@IsArray()
|
||||
@IsDefined()
|
||||
@ValidateNested()
|
||||
@Type(() => EUser)
|
||||
users: EUser[];
|
||||
|
||||
@IsInt()
|
||||
@IsPositive()
|
||||
@IsDefined()
|
||||
total: number;
|
||||
}
|
||||
|
||||
// UserMe
|
||||
export class UserMeResponse {
|
||||
@IsDefined()
|
||||
@ValidateNested()
|
||||
@Type(() => EUser)
|
||||
|
@ -67,8 +100,16 @@ export class AuthMeResponse {
|
|||
newJwtToken: string;
|
||||
}
|
||||
|
||||
export class AuthUserInfoRequest {
|
||||
// UserUpdateRoles
|
||||
export class UserUpdateRolesRequest {
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
username: string;
|
||||
|
||||
@IsArray()
|
||||
@IsDefined()
|
||||
@IsString({ each: true })
|
||||
roles: Roles;
|
||||
}
|
||||
|
||||
export class UserUpdateRolesResponse extends EUser {}
|
|
@ -1,4 +1,3 @@
|
|||
import { IsNotEmpty } from 'class-validator';
|
||||
import tuple from '../types/tuple';
|
||||
|
||||
const SysPreferencesTuple = tuple(
|
||||
|
@ -9,8 +8,3 @@ const SysPreferencesTuple = tuple(
|
|||
|
||||
export const SysPreferences: string[] = SysPreferencesTuple;
|
||||
export type SysPreferences = typeof SysPreferencesTuple[number];
|
||||
|
||||
export class UpdateSysPreferenceRequest {
|
||||
@IsNotEmpty()
|
||||
value: string;
|
||||
}
|
||||
|
|
|
@ -14,6 +14,8 @@ export type Failable<T> = T | Failure;
|
|||
|
||||
export type AsyncFailable<T> = Promise<Failable<T>>;
|
||||
|
||||
// TODO: prevent promise from being allowed in these 2 functions
|
||||
|
||||
export function HasFailed<T>(failable: Failable<T>): failable is Failure {
|
||||
return failable instanceof Failure;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue