add image list api

This commit is contained in:
rubikscraft
2022-05-03 21:37:20 +02:00
parent 71beb9f41c
commit dce72d0680
16 changed files with 119 additions and 60 deletions

View File

@@ -49,6 +49,7 @@ export class ImageDBService {
public async findMany(
count: number,
page: number,
userid: string | false,
): AsyncFailable<EImageBackend[]> {
if (count < 1 || page < 0) return Fail('Invalid page');
if (count > 100) return Fail('Too many results');
@@ -57,6 +58,9 @@ export class ImageDBService {
const found = await this.imageRepo.find({
skip: count * page,
take: count,
where: {
user_id: userid === false ? undefined : userid,
},
});
if (found === undefined) return Fail('Images not found');

View File

@@ -33,10 +33,18 @@ export class ImageManagerService {
private readonly sysPref: SysPreferenceService,
) {}
public async retrieveInfo(id: string): AsyncFailable<EImageBackend> {
public async findOne(id: string): AsyncFailable<EImageBackend> {
return await this.imagesService.findOne(id);
}
public async findMany(
count: number,
page: number,
userid: string | false,
): AsyncFailable<EImageBackend[]> {
return await this.imagesService.findMany(count, page, userid);
}
public async upload(
image: Buffer,
userid: string,

View File

@@ -6,6 +6,7 @@ import {
Logger,
Post
} from '@nestjs/common';
import { PagedRequest } from 'picsur-shared/dist/dto/api/common.dto';
import {
GetSpecialUsersResponse,
UserCreateRequest,
@@ -14,7 +15,6 @@ import {
UserDeleteResponse,
UserInfoRequest,
UserInfoResponse,
UserListRequest,
UserListResponse,
UserUpdateRequest,
UserUpdateResponse
@@ -38,20 +38,9 @@ export class UserManageController {
constructor(private usersService: UsersService) {}
@Get('list')
@Returns(UserListResponse)
async listUsers(): Promise<UserListResponse> {
return this.listUsersPaged({
count: 20,
page: 0,
});
}
@Post('list')
@Returns(UserListResponse)
async listUsersPaged(
@Body() body: UserListRequest,
): Promise<UserListResponse> {
async listUsersPaged(@Body() body: PagedRequest): Promise<UserListResponse> {
const users = await this.usersService.findMany(body.count, body.page);
if (HasFailed(users)) {
this.logger.warn(users.getReason());

View File

@@ -0,0 +1,66 @@
import {
Body,
Controller,
InternalServerErrorException,
Logger,
Post
} from '@nestjs/common';
import { PagedRequest } from 'picsur-shared/dist/dto/api/common.dto';
import { ImageListResponse, ImageUploadResponse } from 'picsur-shared/dist/dto/api/image-manage.dto';
import { Permission } from 'picsur-shared/dist/dto/permissions.dto';
import { HasFailed } from 'picsur-shared/dist/types';
import { MultiPart } from '../../decorators/multipart/multipart.decorator';
import { RequiredPermissions } from '../../decorators/permissions.decorator';
import { ReqUserID } from '../../decorators/request-user.decorator';
import { Returns } from '../../decorators/returns.decorator';
import { ImageManagerService } from '../../managers/image/image.service';
import { ImageUploadDto } from '../../models/dto/image-upload.dto';
@Controller('api/image')
@RequiredPermissions(Permission.ImageUpload)
export class ImageManageController {
private readonly logger = new Logger('ImageManageController');
constructor(private readonly imagesService: ImageManagerService) {}
@Post('upload')
@Returns(ImageUploadResponse)
async uploadImage(
@MultiPart() multipart: ImageUploadDto,
@ReqUserID() userid: string,
): Promise<ImageUploadResponse> {
const image = await this.imagesService.upload(
multipart.image.buffer,
userid,
);
if (HasFailed(image)) {
this.logger.warn(image.getReason(), image.getStack());
throw new InternalServerErrorException('Could not upload image');
}
return image;
}
@Post('my/list')
@Returns(ImageListResponse)
async listUsersPaged(
@Body() body: PagedRequest,
@ReqUserID() userid: string,
): Promise<ImageListResponse> {
const images = await this.imagesService.findMany(
body.count,
body.page,
userid,
);
if (HasFailed(images)) {
this.logger.warn(images.getReason());
throw new InternalServerErrorException('Could not list images');
}
return {
images,
count: images.length,
page: body.page,
};
}
}

View File

@@ -4,29 +4,23 @@ import {
Head,
InternalServerErrorException,
Logger,
NotFoundException,
Post,
Query,
Res,
NotFoundException, Query,
Res
} from '@nestjs/common';
import { FastifyReply } from 'fastify';
import {
ImageMetaResponse,
ImageRequestParams,
ImageUploadResponse,
ImageRequestParams
} from 'picsur-shared/dist/dto/api/image.dto';
import { HasFailed } from 'picsur-shared/dist/types';
import { UsersService } from '../../collections/user-db/user-db.service';
import { ImageFullIdParam } from '../../decorators/image-id/image-full-id.decorator';
import { ImageIdParam } from '../../decorators/image-id/image-id.decorator';
import { MultiPart } from '../../decorators/multipart/multipart.decorator';
import { RequiredPermissions } from '../../decorators/permissions.decorator';
import { ReqUserID } from '../../decorators/request-user.decorator';
import { Returns } from '../../decorators/returns.decorator';
import { ImageManagerService } from '../../managers/image/image.service';
import { ImageFullId } from '../../models/constants/image-full-id.const';
import { Permission } from '../../models/constants/permissions.const';
import { ImageUploadDto } from '../../models/dto/image-upload.dto';
import { EUserBackend2EUser } from '../../models/transformers/user.transformer';
// This is the only controller with CORS enabled
@@ -95,7 +89,7 @@ export class ImageController {
@Get('meta/:id')
@Returns(ImageMetaResponse)
async getImageMeta(@ImageIdParam() id: string): Promise<ImageMetaResponse> {
const image = await this.imagesService.retrieveInfo(id);
const image = await this.imagesService.findOne(id);
if (HasFailed(image)) {
this.logger.warn(image.getReason());
throw new NotFoundException('Could not find image');
@@ -116,23 +110,4 @@ export class ImageController {
return { image, user: EUserBackend2EUser(imageUser), fileMimes };
}
@Post()
@Returns(ImageUploadResponse)
@RequiredPermissions(Permission.ImageUpload)
async uploadImage(
@MultiPart() multipart: ImageUploadDto,
@ReqUserID() userid: string,
): Promise<ImageUploadResponse> {
const image = await this.imagesService.upload(
multipart.image.buffer,
userid,
);
if (HasFailed(image)) {
this.logger.warn(image.getReason(), image.getStack());
throw new InternalServerErrorException('Could not upload image');
}
return image;
}
}

View File

@@ -2,10 +2,11 @@ import { Module } from '@nestjs/common';
import { UsersModule } from '../../collections/user-db/user-db.module';
import { DecoratorsModule } from '../../decorators/decorators.module';
import { ImageManagerModule } from '../../managers/image/image.module';
import { ImageManageController } from './image-manage.controller';
import { ImageController } from './image.controller';
@Module({
imports: [ImageManagerModule, UsersModule, DecoratorsModule],
controllers: [ImageController],
controllers: [ImageController, ImageManageController],
})
export class ImageModule {}

View File

@@ -12,6 +12,7 @@ export const UIFriendlyPermissions: {
[Permission.Settings]: 'View settings',
[Permission.ImageManage]: 'Manage All Images',
[Permission.UserManage]: 'Manage users',
[Permission.RoleManage]: 'Manage roles',
[Permission.SysPrefManage]: 'Manage system',

View File

@@ -53,7 +53,7 @@ export class ViewComponent implements OnInit {
public imageUser: EUser | null = null;
public timeAgo = rxjs_poll(
1000,
10000,
(() => moment(this.image?.created).fromNow()).bind(this)
);

View File

@@ -18,7 +18,7 @@ export class ImageService {
public async UploadImage(image: File): AsyncFailable<string> {
const result = await this.api.postForm(
ImageUploadResponse,
'/i',
'/api/image/upload',
new ImageUploadRequest(image)
);

View File

@@ -200,7 +200,7 @@ export class UtilService {
} else {
this.logger.error(e);
this.showSnackBar(
'An error occured while sharing the image',
'Could not share',
SnackBarType.Error
);
}

View File

@@ -0,0 +1,9 @@
import { z } from 'zod';
import { createZodDto } from '../../util/create-zod-dto';
import { IsPosInt } from '../../validators/positive-int.validator';
export const PagedRequestSchema = z.object({
count: IsPosInt(),
page: IsPosInt(),
});
export class PagedRequest extends createZodDto(PagedRequestSchema) {}

View File

@@ -0,0 +1,16 @@
import { z } from 'zod';
import { EImageSchema } from '../../entities/image.entity';
import { createZodDto } from '../../util/create-zod-dto';
import { IsPosInt } from '../../validators/positive-int.validator';
export const ImageUploadResponseSchema = EImageSchema;
export class ImageUploadResponse extends createZodDto(
ImageUploadResponseSchema,
) {}
export const ImageListResponseSchema = z.object({
images: z.array(EImageSchema),
count: IsPosInt(),
page: IsPosInt(),
});
export class ImageListResponse extends createZodDto(ImageListResponseSchema) {}

View File

@@ -40,8 +40,3 @@ export const ImageMetaResponseSchema = z.object({
}),
});
export class ImageMetaResponse extends createZodDto(ImageMetaResponseSchema) {}
export const ImageUploadResponseSchema = EImageSchema;
export class ImageUploadResponse extends createZodDto(
ImageUploadResponseSchema,
) {}

View File

@@ -8,12 +8,6 @@ import { IsStringList } from '../../validators/string-list.validator';
import { EntityIDObjectSchema } from '../id-object.dto';
// UserList
export const UserListRequestSchema = z.object({
count: IsPosInt(),
page: IsPosInt(),
});
export class UserListRequest extends createZodDto(UserListRequestSchema) {}
export const UserListResponseSchema = z.object({
users: z.array(EUserSchema),
count: IsPosInt(),

View File

@@ -1,12 +1,12 @@
// Config
export enum ImageMime {
QOI = 'image/x-qoi',
JPEG = 'image/jpeg',
PNG = 'image/png',
WEBP = 'image/webp',
TIFF = 'image/tiff',
BMP = 'image/bmp',
// ICO = 'image/x-icon',
QOI = 'image/x-qoi',
}
export enum AnimMime {

View File

@@ -4,7 +4,7 @@
// -> the frontend and backend can be somewhat out of sync
export enum Permission {
ImageView = 'image-view',
ImageUpload = 'image-upload',
ImageUpload = 'image-upload', // Ability to upload and manage own images
UserLogin = 'user-login', // Ability to log in
UserKeepLogin = 'user-keep-login', // Ability to view own user details and refresh token
@@ -12,6 +12,7 @@ export enum Permission {
Settings = 'settings', // Ability to view (personal) settings
ImageManage = 'image-manage', // Ability to manage everyones manage images
UserManage = 'user-manage', // Allow modification of users
RoleManage = 'role-manage', // Allow modification of roles
SysPrefManage = 'syspref-manage',