Skip to content

Commit

Permalink
⚡ feat: Client 정보 API 추가
Browse files Browse the repository at this point in the history
  • Loading branch information
kms0219kms committed Mar 31, 2024
1 parent 2129cbc commit cbcc989
Show file tree
Hide file tree
Showing 11 changed files with 249 additions and 17 deletions.
12 changes: 5 additions & 7 deletions src/app.controller.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { Controller, Get } from '@nestjs/common';
import { ApiOkResponse, ApiOperation, ApiTags } from '@nestjs/swagger';
import { ApiOperation, ApiProperty, ApiTags } from '@nestjs/swagger';

class rootAccessDto {
/**
* Default return
* @example hacking
*/
happy = 'hacking';
@ApiProperty({ example: 'hacking' })
happy: string = 'hacking';
}

@ApiTags('Common - Health Check API')
Expand All @@ -14,11 +16,7 @@ export class AppController {
@Get()
@ApiOperation({
summary: 'Health Check',
description: '서버 상태를 확인합니다.',
})
@ApiOkResponse({
description: '서버가 정상적으로 작동 할 경우',
type: rootAccessDto,
description: 'Check the server is running',
})
rootAccess(): rootAccessDto {
return { happy: 'hacking' };
Expand Down
5 changes: 4 additions & 1 deletion src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';

import { AppController } from './app.controller';
import { RepositoryModule } from './common/repository/repository.module';

import { AuthModule } from './auth/auth.module';
import { RepositoryModule } from './common/repository/repository.module';
import { ClientModule } from './client/client.module';
import { ArtistModule } from './artist/artist.module';
import { ChartsModule } from './charts/charts.module';
import { LyricsModule } from './lyrics/lyrics.module';
Expand All @@ -18,12 +19,14 @@ import { SongsModule } from './songs/songs.module';
envFilePath: ['.env.development', '.env'],
}),
AuthModule,
ClientModule,
ArtistModule,
ChartsModule,
LyricsModule,
MypageModule,
SongsModule,
RepositoryModule,
AppModule,
],
controllers: [AppController],
})
Expand Down
44 changes: 44 additions & 0 deletions src/client/client.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { Controller, Get, Logger, Query } from '@nestjs/common';
import { ApiOperation, ApiTags } from '@nestjs/swagger';

import { ClientService } from './client.service';
import { APIException } from 'src/common/dto/APIException.dto';

@ApiTags('Common - Client Information')
@Controller('client')
export class ClientController {
private readonly logger = new Logger(ClientController.name);

constructor(private readonly clientService: ClientService) {}

@Get('maintenance')
@ApiOperation({
summary: '현재 점검여부 확인',
description: '왁타플레이 서비스가 현재 점검 중인지 확인합니다.',
})
async maintenance() {
return await this.clientService.getMaintenance();
// return {};
}

@Get('update')
@ApiOperation({
summary: '클라이언트 업데이트 확인',
description: '사용자의 클라이언트에 업데이트가 존재하는지 확인합니다.',
})
async update(@Query('os') os: string, @Query('version') version: string) {
if (!os || !version) {
throw new APIException(400, "'os'와 'version' 파라미터는 필수입니다.");
}

if (!['ios', 'android', 'pc'].includes(os)) {
throw new APIException(
400,
"'os' 파라미터는 'ios' 또는 'android', 'pc' 만 허용됩니다.",
);
}

return await this.clientService.update(os, version);
// return {};
}
}
14 changes: 14 additions & 0 deletions src/client/client.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { Module } from '@nestjs/common';
import { ClientController } from './client.controller';
import { ClientService } from './client.service';

import { RepositoryModule } from 'src/common/repository/repository.module';
import { maintenanceProviders } from 'src/common/repository/models/maintenance.providers';
import { versionProviders } from 'src/common/repository/models/version.providers';

@Module({
imports: [RepositoryModule],
controllers: [ClientController],
providers: [ClientService, ...maintenanceProviders, ...versionProviders],
})
export class ClientModule {}
63 changes: 63 additions & 0 deletions src/client/client.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { Inject, Injectable, Logger } from '@nestjs/common';
import { Model } from 'mongoose';

import { IMaintenance } from 'src/common/repository/schemas/maintenance.schema';
import { IVersion } from 'src/common/repository/schemas/version.schema';

import { UpdateResponseDto } from './dto/updateResponse.dto';
import { APIException } from 'src/common/dto/APIException.dto';

@Injectable()
export class ClientService {
private readonly logger = new Logger(ClientService.name);

constructor(
@Inject('MAINTENANCE_MODEL')
private readonly maintenanceModel: Model<IMaintenance>,
@Inject('CLIENTVERSION_MODEL')
private readonly clientVersionModel: Model<IVersion>,
) {}

async getMaintenance(): Promise<IMaintenance> {
const maintenanceData = await this.maintenanceModel
.find()
.select({ _id: 0, __v: 0 })
.sort({ date: -1 });

return (
maintenanceData.find(
(maintenance) => maintenance.date.end >= new Date(),
) || null
);
}

async update(os: string, version: string): Promise<UpdateResponseDto> {
const versions = await this.clientVersionModel.find({ os });
const updateUrl = {
ios: 'https://apps.apple.com/kr/app/id',
android:
'https://play.google.com/store/apps/details?id=com.waktaplay.mobile',
pc: null,
}[os];

if (!versions.length) {
throw new APIException(404, '업데이트 정보를 찾을 수 없습니다.');
}

const latestVersion = versions.reduce((prev, curr) =>
prev.version > curr.version ? prev : curr,
);

return {
// 업데이트가 필요한지 여부
// 현재 버전이 최신 버전보다 낮은 경우 true
needUpdate: version < latestVersion.version,

os: latestVersion.os,
updateUrl,

version: latestVersion.version,
specialLogo: latestVersion.specialLogo,
};
}
}
33 changes: 33 additions & 0 deletions src/client/dto/updateResponse.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
export class UpdateResponseDto {
/**
* 업데이트 필요 여부
* @example true
*/
needUpdate: boolean;

/**
* 업데이트 OS 정보
* @example ios
*/
os: string;

/**
* 업데이트 URL
* @example https://play.google.com/store/apps/details?id=com.waktaplay.mobile
*/
updateUrl?: string;

/**
* 업데이트 버전
* @example "1.0.0"
*/
version: string;

/**
* 이벤트 특별 로고 URL
* @example https://cdn.waktaplay.com/lottie/2024/christmas.json
*/
specialLogo?: string;
}

export default UpdateResponseDto;
21 changes: 12 additions & 9 deletions src/common/filter/global-exception.filter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,32 +17,35 @@ export class GlobalExceptionFilter implements ExceptionFilter {
const ctx = host.switchToHttp();
const response: FastifyReply<any> = ctx.getResponse<FastifyReply>();

const responseAt: string = new Date().toISOString();

let status: HttpStatus = HttpStatus.INTERNAL_SERVER_ERROR;
let message: string | object | APIException =
'내부 서버 오류가 발생했습니다.';

if (exception instanceof HttpException) {
status = exception.getStatus();
message = exception.getResponse();
} else if (exception instanceof APIException) {
status = exception.status;
message = exception;
}

if (message instanceof APIException) {
response.status(status).send({
code: HttpStatus[message.status],
status: message.status,
message: message.message,
data: message.data,
if (exception instanceof APIException) {
response.status(exception.status).send({
code: HttpStatus[exception.status],
status: exception.status,

data: exception.data,
message: exception.message,
responseAt: responseAt,
});
return;
}

response.status(status).send({
code: HttpStatus[status],
status: status,

message: message['message'] || message['error'] || message,
responseAt: responseAt,
});
}
}
11 changes: 11 additions & 0 deletions src/common/repository/models/maintenance.providers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { Connection } from 'mongoose';
import { MaintenanceSchema } from '../schemas/maintenance.schema';

export const maintenanceProviders = [
{
provide: 'MAINTENANCE_MODEL',
useFactory: (connection: Connection) =>
connection.model('Maintenance', MaintenanceSchema),
inject: ['DATABASE_CONNECTION'],
},
];
11 changes: 11 additions & 0 deletions src/common/repository/models/version.providers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { Connection } from 'mongoose';
import { VersionSchema } from '../schemas/version.schema';

export const versionProviders = [
{
provide: 'CLIENTVERSION_MODEL',
useFactory: (connection: Connection) =>
connection.model('ClientVersion', VersionSchema),
inject: ['DATABASE_CONNECTION'],
},
];
33 changes: 33 additions & 0 deletions src/common/repository/schemas/maintenance.schema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import mongoose from 'mongoose';

export interface IMaintenance {
type: 'SCHEDULED' | 'NONSTOP' | 'EMERGENCY' | 'END_OF_LIFE';
title: string;
description?: string;
date: {
start: Date;
end: Date;
};
}

export const MaintenanceSchema = new mongoose.Schema<IMaintenance>({
type: {
type: String,
required: true,
},
title: {
type: String,
required: true,
},
description: String,
date: {
start: {
type: Date,
required: true,
},
end: {
type: Date,
required: true,
},
},
});
19 changes: 19 additions & 0 deletions src/common/repository/schemas/version.schema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import mongoose from 'mongoose';

export interface IVersion {
os: string;
version: string;
specialLogo?: string;
}

export const VersionSchema = new mongoose.Schema<IVersion>({
os: {
type: String,
required: true,
},
version: {
type: String,
required: true,
},
specialLogo: String,
});

0 comments on commit cbcc989

Please sign in to comment.