Skip to content

Commit 809d47c

Browse files
authored
Merge pull request #79 from gsainfoteam/yhkim13/pgb-43
yhkim13/pgb-43 to develop
2 parents 31cac31 + f979a82 commit 809d47c

File tree

8 files changed

+82
-17
lines changed

8 files changed

+82
-17
lines changed

drizzle/schema/device.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,11 @@
1-
import { pgTable, text, timestamp, uuid, varchar } from "drizzle-orm/pg-core";
1+
import {
2+
boolean,
3+
pgTable,
4+
text,
5+
timestamp,
6+
uuid,
7+
varchar,
8+
} from "drizzle-orm/pg-core";
29
import { relations } from "drizzle-orm";
310
import { users } from "./users";
411
import { userAlarmSetting } from "./user-alarm-setting";
@@ -12,7 +19,8 @@ CREATE TABLE "device" (
1219
"created_at" timestamp with time zone NOT NULL DEFAULT NOW(),
1320
"updated_at" timestamp with time zone NOT NULL DEFAULT NOW(),
1421
"device_id" varchar(64) NOT NULL,
15-
"fcm_token" text NOT NULL
22+
"fcm_token" text NOT NULL,
23+
"logged_in" boolean NOT NULL DEFAULT FALSE
1624
);
1725
*/
1826
export const device = pgTable("device", {
@@ -28,6 +36,7 @@ export const device = pgTable("device", {
2836
.defaultNow(),
2937
deviceId: varchar("device_id", { length: 64 }).notNull(),
3038
fcmToken: text("fcm_token").notNull(),
39+
loggedIn: boolean("logged_in").notNull().default(false),
3140
});
3241

3342
export const deviceRelations = relations(device, ({ one, many }) => ({

drizzle/schema/schema.sql

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,3 +274,5 @@ ALTER TABLE "user_consent"
274274
CREATE INDEX "idx_popo_chat_reservation_pot_fk_popo_chat_msg_type"
275275
ON "popo_chat_reservation" ("pot_fk", "popo_chat_msg_type");
276276

277+
ALTER TABLE "device"
278+
ADD COLUMN "logged_in" boolean NOT NULL DEFAULT FALSE;

src/database/entity/device.entity.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,5 @@ export class DeviceEntity {
1010
version: string;
1111
createdAt?: Date;
1212
updatedAt?: Date;
13+
loggedIn: boolean;
1314
}

src/database/repository/device.repository.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ export class DeviceRepository {
5656
SELECT d.fcm_token, uas.chat_push, uas.marketing_push, uas.pot_in_out_push
5757
FROM device AS d
5858
LEFT OUTER JOIN user_alarm_setting AS uas ON d.pk = uas.device_fk
59-
WHERE d.user_fk in (?1);
59+
WHERE d.user_fk in (?1) AND d.logged_in = true;
6060
*/
6161
async findFcmTokensByUserFks(userFks: string[]) {
6262
return await this.dbService.db
@@ -68,7 +68,7 @@ export class DeviceRepository {
6868
})
6969
.from(device)
7070
.leftJoin(userAlarmSetting, eq(device.pk, userAlarmSetting.deviceFk))
71-
.where(inArray(device.userFk, userFks));
71+
.where(and(inArray(device.userFk, userFks), eq(device.loggedIn, true)));
7272
}
7373

7474
async insert(
@@ -84,6 +84,7 @@ export class DeviceRepository {
8484
fcmToken: deviceEntity.fcmToken,
8585
os: deviceEntity.os,
8686
version: deviceEntity.version,
87+
loggedIn: deviceEntity.loggedIn,
8788
})
8889
.returning();
8990

@@ -103,6 +104,7 @@ export class DeviceRepository {
103104
os: deviceEntity.os,
104105
version: deviceEntity.version,
105106
updatedAt: new Date(),
107+
loggedIn: deviceEntity.loggedIn,
106108
})
107109
.where(eq(device.pk, deviceEntity.pk));
108110
}
@@ -117,6 +119,7 @@ export class DeviceRepository {
117119
version: result.version,
118120
createdAt: result.createdAt,
119121
updatedAt: result.updatedAt,
122+
loggedIn: result.loggedIn,
120123
};
121124
}
122125
}

src/database/repository/refresh-token.repository.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,12 @@ export class RefreshTokenRepository {
5050
return this.resultToRefreshTokenEntity(insertedRefreshToken);
5151
}
5252

53+
async deleteByOpaqueHash(opaqueHash: string, tx: TxType): Promise<void> {
54+
await tx
55+
.delete(refreshToken)
56+
.where(eq(refreshToken.opaqueHash, opaqueHash));
57+
}
58+
5359
private resultToRefreshTokenEntity(result: any): RefreshTokenEntity {
5460
return {
5561
opaqueHash: result.opaqueHash,

src/user/dto/logout.dto.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { IsNotEmpty, IsString } from "class-validator";
2+
3+
export class LogoutRequestDto {
4+
@IsNotEmpty()
5+
@IsString()
6+
refresh_token: string;
7+
}

src/user/user.controller.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { UserGuard } from "@src/auth/guard/user.guard";
1313
import { GetUser } from "@src/global/decorator/get-user.decorator";
1414
import { UserContext } from "@src/auth/user-context.entity";
1515
import { UpdateConsentDto } from "@src/user/dto/update-consent.dto";
16+
import { LogoutRequestDto } from "@src/user/dto/logout.dto";
1617

1718
@Controller("/api/v1/user")
1819
export class UserController {
@@ -65,4 +66,13 @@ export class UserController {
6566
): Promise<BaseResultDto> {
6667
return this.userService.consent(req, userCtx);
6768
}
69+
70+
@Post("/logout")
71+
@UseGuards(UserGuard)
72+
async logout(
73+
@Body() req: LogoutRequestDto,
74+
@GetUser() userCtx: UserContext,
75+
): Promise<BaseResultDto> {
76+
return this.userService.logout(req, userCtx);
77+
}
6878
}

src/user/user.service.ts

Lines changed: 40 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
import { Injectable } from "@nestjs/common";
1+
import {
2+
ForbiddenException,
3+
Injectable,
4+
InternalServerErrorException,
5+
} from "@nestjs/common";
26
import { LoginRequestDto, LoginResponseDto } from "@src/user/dto/login.dto";
37
import { BaseResultDto } from "@src/global/dto/base-result.dto";
48
import { SetDeviceInfoRequestDto } from "@src/user/dto/set-fcm.dto";
@@ -21,6 +25,8 @@ import { UserEntity } from "@src/database/entity/user.entity";
2125
import { UpdateConsentDto } from "@src/user/dto/update-consent.dto";
2226
import { UserConsentRepository } from "@src/database/repository/user-consent.repository";
2327
import { UserConsentEntity } from "@src/database/entity/user-consent.entity";
28+
import { LogoutRequestDto } from "@src/user/dto/logout.dto";
29+
import { RefreshTokenRepository } from "@src/database/repository/refresh-token.repository";
2430

2531
@Injectable()
2632
export class UserService {
@@ -32,6 +38,7 @@ export class UserService {
3238
private readonly deviceRepository: DeviceRepository,
3339
private readonly userAlarmSettingRepository: UserAlarmSettingRepository,
3440
private readonly userConsentRepository: UserConsentRepository,
41+
private readonly refreshTokenRepository: RefreshTokenRepository,
3542
) {}
3643

3744
async login(
@@ -105,27 +112,20 @@ export class UserService {
105112
userCtx.userId,
106113
);
107114
if (!device) {
108-
throw new Error("Device not found"); // TODO
115+
throw new ForbiddenException("Device not found");
109116
}
110117

111-
let updated = false;
112-
113118
if (!!fcmToken) {
114119
device.fcmToken = fcmToken;
115-
updated = true;
116120
}
117121
if (!!os) {
118122
device.os = os;
119-
updated = true;
120123
}
121124
if (!!version) {
122125
device.version = version;
123-
updated = true;
124126
}
125127

126-
if (!updated) {
127-
return BaseResultDto.OK; // 변경된 정보가 없으면 바로 반환
128-
}
128+
device.loggedIn = true;
129129

130130
await this.dbService.db.transaction(async (tx: TxType) => {
131131
await this.deviceRepository.update(device, tx);
@@ -263,6 +263,32 @@ export class UserService {
263263
return BaseResultDto.OK;
264264
}
265265

266+
async logout(
267+
req: LogoutRequestDto,
268+
userCtx: UserContext,
269+
): Promise<BaseResultDto> {
270+
const device = await this.deviceRepository.findByPkAndUserFk(
271+
userCtx.devicePk,
272+
userCtx.userId,
273+
);
274+
275+
if (!device) {
276+
return BaseResultDto.OK;
277+
}
278+
279+
device.loggedIn = false;
280+
281+
await this.dbService.db.transaction(async (tx: TxType) => {
282+
await this.refreshTokenRepository.deleteByOpaqueHash(
283+
req.refresh_token,
284+
tx,
285+
);
286+
await this.deviceRepository.update(device, tx);
287+
});
288+
289+
return BaseResultDto.OK;
290+
}
291+
266292
private async createNewUser(
267293
sub: string,
268294
name: string,
@@ -280,7 +306,7 @@ export class UserService {
280306
);
281307

282308
if (!user) {
283-
throw new Error("Failed to create new user"); // TODO
309+
throw new InternalServerErrorException("Failed to create new user");
284310
}
285311

286312
return user;
@@ -297,8 +323,9 @@ export class UserService {
297323
userFk: user.pk,
298324
fcmToken: "", // 초기값은 빈 문자열로 설정
299325
deviceId: deviceId,
300-
os: "iOS", // OS 정보는 추후에 업데이트 필요
301-
version: "0.0.1", // 초기 버전 정보
326+
os: "", // OS 정보는 추후에 업데이트 필요
327+
version: "", // 초기 버전 정보
328+
loggedIn: false,
302329
},
303330
tx,
304331
);

0 commit comments

Comments
 (0)