Init repo
This commit is contained in:
22
src/app.controller.spec.ts
Normal file
22
src/app.controller.spec.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { AppController } from './app.controller';
|
||||
import { AppService } from './app.service';
|
||||
|
||||
describe('AppController', () => {
|
||||
let appController: AppController;
|
||||
|
||||
beforeEach(async () => {
|
||||
const app: TestingModule = await Test.createTestingModule({
|
||||
controllers: [AppController],
|
||||
providers: [AppService],
|
||||
}).compile();
|
||||
|
||||
appController = app.get<AppController>(AppController);
|
||||
});
|
||||
|
||||
describe('root', () => {
|
||||
it('should return "Hello World!"', () => {
|
||||
expect(appController.getHello()).toBe('Hello World!');
|
||||
});
|
||||
});
|
||||
});
|
||||
12
src/app.controller.ts
Normal file
12
src/app.controller.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { Controller, Get } from '@nestjs/common';
|
||||
import { AppService } from './app.service';
|
||||
|
||||
@Controller()
|
||||
export class AppController {
|
||||
constructor(private readonly appService: AppService) {}
|
||||
|
||||
@Get()
|
||||
getHello(): string {
|
||||
return this.appService.getHello();
|
||||
}
|
||||
}
|
||||
12
src/app.module.ts
Normal file
12
src/app.module.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { PrismaModule } from './prisma/prisma.module';
|
||||
import { AppController } from './app.controller';
|
||||
import { AppService } from './app.service';
|
||||
import { VideosModule } from './videos/videos.module';
|
||||
|
||||
@Module({
|
||||
imports: [PrismaModule, VideosModule],
|
||||
controllers: [AppController],
|
||||
providers: [AppService],
|
||||
})
|
||||
export class AppModule {}
|
||||
8
src/app.service.ts
Normal file
8
src/app.service.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
@Injectable()
|
||||
export class AppService {
|
||||
getHello(): string {
|
||||
return 'Hello World!';
|
||||
}
|
||||
}
|
||||
8
src/main.ts
Normal file
8
src/main.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import { NestFactory } from '@nestjs/core';
|
||||
import { AppModule } from './app.module';
|
||||
|
||||
async function bootstrap() {
|
||||
const app = await NestFactory.create(AppModule);
|
||||
await app.listen(process.env.PORT ?? 3000);
|
||||
}
|
||||
bootstrap();
|
||||
8
src/prisma/prisma.module.ts
Normal file
8
src/prisma/prisma.module.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { PrismaService } from './prisma.service';
|
||||
|
||||
@Module({
|
||||
providers: [PrismaService],
|
||||
exports: [PrismaService],
|
||||
})
|
||||
export class PrismaModule {}
|
||||
16
src/prisma/prisma.service.ts
Normal file
16
src/prisma/prisma.service.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { Injectable, OnModuleInit, OnModuleDestroy } from '@nestjs/common';
|
||||
import { PrismaClient } from 'generated/prisma';
|
||||
|
||||
@Injectable()
|
||||
export class PrismaService
|
||||
extends PrismaClient
|
||||
implements OnModuleInit, OnModuleDestroy
|
||||
{
|
||||
async onModuleInit(): Promise<void> {
|
||||
await this.$connect();
|
||||
}
|
||||
|
||||
async onModuleDestroy(): Promise<void> {
|
||||
await this.$disconnect();
|
||||
}
|
||||
}
|
||||
59
src/videos/dto/create-video-dto.ts
Normal file
59
src/videos/dto/create-video-dto.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
import {
|
||||
IsEnum,
|
||||
IsInt,
|
||||
IsISO8601,
|
||||
IsOptional,
|
||||
IsString,
|
||||
IsUrl,
|
||||
MaxLength,
|
||||
} from 'class-validator';
|
||||
import { EVideoSituation } from '../../../generated/prisma';
|
||||
|
||||
export class CreateVideoDto {
|
||||
@IsUrl()
|
||||
@MaxLength(244)
|
||||
url!: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsEnum(EVideoSituation)
|
||||
situation?: EVideoSituation;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
@MaxLength(244)
|
||||
error_message?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsInt()
|
||||
clips_quantity?: number;
|
||||
|
||||
@IsOptional()
|
||||
times?: unknown;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
@MaxLength(244)
|
||||
title?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
@MaxLength(244)
|
||||
filename?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
@MaxLength(244)
|
||||
videoid?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsISO8601()
|
||||
datetime_download?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsISO8601()
|
||||
datetime_convert?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsISO8601()
|
||||
datetime_posted?: string;
|
||||
}
|
||||
29
src/videos/dto/paginated.dto.ts
Normal file
29
src/videos/dto/paginated.dto.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import { Type } from 'class-transformer';
|
||||
import { IsInt, IsOptional, Max, Min } from 'class-validator';
|
||||
|
||||
export class PaginatedQueryDto {
|
||||
@IsOptional()
|
||||
@Type(() => Number)
|
||||
@IsInt()
|
||||
@Min(1)
|
||||
page: number = 1;
|
||||
|
||||
@IsOptional()
|
||||
@Type(() => Number)
|
||||
@IsInt()
|
||||
@Min(1)
|
||||
@Max(100)
|
||||
perPage: number = 20;
|
||||
}
|
||||
|
||||
export type PaginatedResponse<T> = {
|
||||
content: T[];
|
||||
pagination: {
|
||||
page: number;
|
||||
perPage: number;
|
||||
total: number;
|
||||
totalPages: number;
|
||||
hasNext: boolean;
|
||||
hasPrev: boolean;
|
||||
};
|
||||
};
|
||||
12
src/videos/dto/video-response.dto.ts
Normal file
12
src/videos/dto/video-response.dto.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { EVideoSituation } from 'generated/prisma';
|
||||
|
||||
export interface VideoResponseDto {
|
||||
id: number;
|
||||
title: string | null;
|
||||
url: string;
|
||||
situation: EVideoSituation;
|
||||
clips_quantity: number;
|
||||
videoid: string;
|
||||
filename: string;
|
||||
datetime_download: Date | string;
|
||||
}
|
||||
54
src/videos/videos.controller.ts
Normal file
54
src/videos/videos.controller.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
import {
|
||||
Controller,
|
||||
Get,
|
||||
Param,
|
||||
Patch,
|
||||
Delete,
|
||||
Body,
|
||||
Query,
|
||||
} from '@nestjs/common';
|
||||
import { videos, Prisma, EVideoSituation } from 'generated/prisma';
|
||||
|
||||
import { VideosService } from './videos.service';
|
||||
import { VideoResponseDto } from './dto/video-response.dto';
|
||||
import { PaginatedQueryDto, PaginatedResponse } from './dto/paginated.dto';
|
||||
|
||||
@Controller('videos')
|
||||
export class VideosController {
|
||||
constructor(private readonly videosService: VideosService) {}
|
||||
|
||||
@Get()
|
||||
async list(
|
||||
@Query() query: PaginatedQueryDto,
|
||||
@Query('situation') situation?: EVideoSituation,
|
||||
): Promise<PaginatedResponse<VideoResponseDto>> {
|
||||
return this.videosService.listPaginated(
|
||||
query.page,
|
||||
query.perPage,
|
||||
situation,
|
||||
);
|
||||
}
|
||||
|
||||
@Get(':id')
|
||||
async get(@Param('id') id: string): Promise<videos | null> {
|
||||
return this.videosService.get(Number(id));
|
||||
}
|
||||
|
||||
@Patch(':id')
|
||||
async update(
|
||||
@Param('id') id: string,
|
||||
@Body() body: Prisma.videosUpdateInput,
|
||||
): Promise<videos> {
|
||||
return this.videosService.update(Number(id), body);
|
||||
}
|
||||
|
||||
@Delete(':id')
|
||||
async delete(@Param('id') id: string): Promise<videos> {
|
||||
return this.videosService.delete(Number(id));
|
||||
}
|
||||
|
||||
@Get('situation/:s')
|
||||
async listBySituation(@Param('s') s: EVideoSituation): Promise<videos[]> {
|
||||
return this.videosService.listBySituation(s);
|
||||
}
|
||||
}
|
||||
11
src/videos/videos.module.ts
Normal file
11
src/videos/videos.module.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { VideosService } from './videos.service';
|
||||
import { VideosController } from './videos.controller';
|
||||
import { PrismaModule } from '../prisma/prisma.module';
|
||||
|
||||
@Module({
|
||||
imports: [PrismaModule],
|
||||
providers: [VideosService],
|
||||
controllers: [VideosController],
|
||||
})
|
||||
export class VideosModule {}
|
||||
110
src/videos/videos.service.ts
Normal file
110
src/videos/videos.service.ts
Normal file
@@ -0,0 +1,110 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import dayjs from 'dayjs';
|
||||
|
||||
import { Prisma, videos, EVideoSituation } from 'generated/prisma';
|
||||
|
||||
import { PrismaService } from '../prisma/prisma.service';
|
||||
import { VideoResponseDto } from './dto/video-response.dto';
|
||||
import { PaginatedResponse } from './dto/paginated.dto';
|
||||
|
||||
@Injectable()
|
||||
export class VideosService {
|
||||
constructor(private readonly prisma: PrismaService) {}
|
||||
|
||||
async list(): Promise<VideoResponseDto[]> {
|
||||
const data = await this.prisma.videos.findMany({ orderBy: { id: 'desc' } });
|
||||
|
||||
return data.map((video) => ({
|
||||
id: video.id,
|
||||
title: video.title,
|
||||
url: video.url,
|
||||
situation: video.situation,
|
||||
clips_quantity: video.clips_quantity ?? 0,
|
||||
videoid: video.videoid!,
|
||||
filename: video.filename || '',
|
||||
datetime_download: video.datetime_download
|
||||
? dayjs(video.datetime_download).format('DD/MM/YYYY HH:mm:ss')
|
||||
: '',
|
||||
}));
|
||||
}
|
||||
|
||||
async listPaginated(
|
||||
page: number,
|
||||
perPage: number,
|
||||
situation?: EVideoSituation,
|
||||
): Promise<PaginatedResponse<VideoResponseDto>> {
|
||||
const skip = (page - 1) * perPage;
|
||||
const where = situation ? { situation } : undefined;
|
||||
|
||||
const [rows, total] = await Promise.all([
|
||||
this.prisma.videos.findMany({
|
||||
where,
|
||||
orderBy: { id: 'asc' },
|
||||
skip,
|
||||
take: Number(perPage),
|
||||
select: {
|
||||
id: true,
|
||||
title: true,
|
||||
url: true,
|
||||
situation: true,
|
||||
clips_quantity: true,
|
||||
videoid: true,
|
||||
filename: true,
|
||||
datetime_download: true,
|
||||
},
|
||||
}),
|
||||
this.prisma.videos.count({ where }),
|
||||
]);
|
||||
|
||||
const content: VideoResponseDto[] = rows.map((row) => ({
|
||||
id: row.id,
|
||||
title: row.title ?? null,
|
||||
url: row.url,
|
||||
situation: row.situation,
|
||||
clips_quantity: row.clips_quantity ?? 0,
|
||||
videoid: row.videoid ?? '',
|
||||
filename: row.filename ?? '',
|
||||
datetime_download: row.datetime_download ?? '',
|
||||
}));
|
||||
|
||||
const totalPages = Math.max(1, Math.ceil(total / perPage));
|
||||
|
||||
return {
|
||||
content,
|
||||
pagination: {
|
||||
page,
|
||||
perPage,
|
||||
total,
|
||||
totalPages,
|
||||
hasNext: page < totalPages,
|
||||
hasPrev: page > 1,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
async get(id: number): Promise<videos | null> {
|
||||
return this.prisma.videos.findUnique({
|
||||
where: { id },
|
||||
});
|
||||
}
|
||||
|
||||
async update(id: number, data: Prisma.videosUpdateInput): Promise<videos> {
|
||||
return this.prisma.videos.update({
|
||||
where: { id },
|
||||
data,
|
||||
});
|
||||
}
|
||||
|
||||
async delete(id: number): Promise<videos> {
|
||||
return this.prisma.videos.delete({
|
||||
where: { id },
|
||||
});
|
||||
}
|
||||
|
||||
async listBySituation(situation: EVideoSituation): Promise<videos[]> {
|
||||
return this.prisma.videos.findMany({
|
||||
where: { situation },
|
||||
orderBy: { id: 'desc' },
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user