Skip to content

Commit f2af009

Browse files
committed
add: JwtService and FastifyCookieService in library packages
1 parent 3b8a2eb commit f2af009

File tree

7 files changed

+182
-375
lines changed

7 files changed

+182
-375
lines changed
Lines changed: 4 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,11 @@
11
import { ENV } from '@env'
2-
import type { CookieSerializeOptions } from '@fastify/cookie'
3-
import { FastifyReply } from 'fastify'
2+
import { FastifyCookieService } from '@packages/library/fastifyCookie'
43
import { injectable, singleton } from 'tsyringe'
54

65
@injectable()
76
@singleton()
8-
export class CookieService {
9-
private get domains() {
10-
const isProduction = ENV.appEnv !== 'development'
11-
if (isProduction) return ['.velog.io']
12-
return ['location', undefined]
13-
}
14-
public getCookie(reply: FastifyReply, name: string) {
15-
return reply.cookies[name]
16-
}
17-
public setCookie(
18-
reply: FastifyReply,
19-
name: string,
20-
value: string,
21-
options?: CookieSerializeOptions,
22-
): void {
23-
this.domains.forEach((domain) => {
24-
reply.cookie(name, value, {
25-
httpOnly: true,
26-
domain,
27-
path: '/',
28-
...options,
29-
})
30-
})
31-
}
32-
public clearCookie(reply: FastifyReply, name: string): void {
33-
this.domains.forEach((domain) => {
34-
reply.clearCookie(name, {
35-
domain,
36-
maxAge: 0,
37-
httpOnly: true,
38-
})
39-
})
7+
export class CookieService extends FastifyCookieService {
8+
constructor() {
9+
super({ appEnv: ENV.appEnv })
4010
}
4111
}

apps/server/src/lib/jwt/JwtService.ts

Lines changed: 17 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,28 @@
1-
import jwt, { SignOptions } from 'jsonwebtoken'
1+
import type { SignOptions } from 'jsonwebtoken'
22
import { injectable, singleton } from 'tsyringe'
33
import { Time } from '@constants/TimeConstants.js'
44
import { ENV } from '@env'
55
import { DbService } from '@lib/db/DbService.js'
66
import { UnauthorizedError } from '@errors/UnauthorizedError.js'
7+
import { JwtService as JWTService } from '@packages/library/jwt'
8+
9+
interface Service {
10+
generateUserToken(userId: string): Promise<{ refreshToken: string; accessToken: string }>
11+
refreshUserToken(
12+
userId: string,
13+
tokenId: string,
14+
refreshTokenExp: number,
15+
originalRefreshToken: string,
16+
): Promise<{ accessToken: string; refreshToken: string }>
17+
generateToken(payload: string | Buffer | Record<any, any>, options?: SignOptions): Promise<string>
18+
decodeToken<T = unknown>(token: string): Promise<T>
19+
}
720

821
@injectable()
922
@singleton()
10-
export class JwtService {
11-
constructor(private readonly db: DbService) {}
12-
public generateToken(
13-
payload: string | Buffer | Record<any, any>,
14-
options?: SignOptions,
15-
): Promise<string> {
16-
const jwtOptions: SignOptions = {
17-
issuer: 'velog.io',
18-
expiresIn: '7d',
19-
...options,
20-
}
21-
22-
if (!jwtOptions.expiresIn) {
23-
// removes expiresIn when expiresIn is given as undefined
24-
delete jwtOptions.expiresIn
25-
}
26-
return new Promise((resolve, reject) => {
27-
if (!ENV.jwtSecretKey) return
28-
jwt.sign(payload, ENV.jwtSecretKey, jwtOptions, (err, token) => {
29-
if (err) reject(err)
30-
resolve(token as string)
31-
})
32-
})
33-
}
34-
public decodeToken<T = unknown>(token: string): Promise<T> {
35-
return new Promise((resolve, reject) => {
36-
jwt.verify(token, ENV.jwtSecretKey, (err, decoded) => {
37-
if (err) reject(err)
38-
resolve(decoded as T)
39-
})
40-
})
23+
export class JwtService extends JWTService implements Service {
24+
constructor(private readonly db: DbService) {
25+
super({ secretKey: ENV.jwtSecretKey })
4126
}
4227
public async generateUserToken(userId: string) {
4328
const authToken = await this.db.authToken.create({
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { container } from 'tsyringe'
2+
import { FastifyCookieService } from './FastifyCookieService.mjs'
3+
4+
describe('FastifyCookieService', () => {
5+
const service = container.resolve(FastifyCookieService)
6+
it('should be defined', () => {
7+
expect(service).toBeDefined()
8+
})
9+
})
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import type { CookieSerializeOptions } from '@fastify/cookie'
2+
import { FastifyReply } from 'fastify'
3+
4+
interface Service {
5+
setCookie(
6+
reply: FastifyReply,
7+
name: string,
8+
value: string,
9+
options?: CookieSerializeOptions,
10+
): void
11+
getCookie(reply: FastifyReply, name: string): string | undefined
12+
clearCookie(reply: FastifyReply, name: string): void
13+
}
14+
15+
type Params = {
16+
appEnv: 'development' | 'stage' | 'production'
17+
}
18+
19+
export class FastifyCookieService implements Service {
20+
private appEnv!: 'development' | 'stage' | 'production'
21+
constructor({ appEnv }: Params) {
22+
this.appEnv = appEnv
23+
}
24+
public setCookie(
25+
reply: FastifyReply,
26+
name: string,
27+
value: string,
28+
options?: CookieSerializeOptions,
29+
): void {
30+
this.domains.forEach((domain) => {
31+
reply.cookie(name, value, {
32+
httpOnly: true,
33+
domain,
34+
path: '/',
35+
...options,
36+
})
37+
})
38+
}
39+
public getCookie(reply: FastifyReply, name: string) {
40+
return reply.cookies[name]
41+
}
42+
public clearCookie(reply: FastifyReply, name: string): void {
43+
this.domains.forEach((domain) => {
44+
reply.clearCookie(name, {
45+
domain,
46+
maxAge: 0,
47+
httpOnly: true,
48+
})
49+
})
50+
}
51+
private get domains() {
52+
const isProduction = this.appEnv !== 'development'
53+
if (isProduction) return ['.velog.io']
54+
return ['location', undefined]
55+
}
56+
}

packages/library/src/jwt/Jwt.test.mts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { container } from 'tsyringe'
2+
import { JwtService } from './JwtService.mjs'
3+
4+
describe('JwtService', () => {
5+
const service = container.resolve(JwtService)
6+
it('should be defined', () => {
7+
expect(service).toBeDefined()
8+
})
9+
})
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import jwt, { SignOptions } from 'jsonwebtoken'
2+
3+
interface Service {
4+
generateToken(payload: string | Buffer | Record<any, any>, options?: SignOptions): Promise<string>
5+
decodeToken<T = unknown>(token: string): Promise<T>
6+
}
7+
8+
type Params = {
9+
secretKey: string
10+
}
11+
12+
export class JwtService implements Service {
13+
private secretKey!: string
14+
constructor({ secretKey }: Params) {
15+
this.secretKey = secretKey
16+
}
17+
public generateToken(
18+
payload: string | Buffer | Record<any, any>,
19+
options?: SignOptions,
20+
): Promise<string> {
21+
const jwtOptions: SignOptions = {
22+
issuer: 'velog.io',
23+
expiresIn: '7d',
24+
...options,
25+
}
26+
27+
if (!jwtOptions.expiresIn) {
28+
// removes expiresIn when expiresIn is given as undefined
29+
delete jwtOptions.expiresIn
30+
}
31+
32+
return new Promise((resolve, reject) => {
33+
jwt.sign(payload, this.secretKey, jwtOptions, (err, token) => {
34+
if (err) reject(err)
35+
resolve(token as string)
36+
})
37+
})
38+
}
39+
public decodeToken<T = unknown>(token: string): Promise<T> {
40+
return new Promise((resolve, reject) => {
41+
jwt.verify(token, this.secretKey, (err, decoded) => {
42+
if (err) reject(err)
43+
resolve(decoded as T)
44+
})
45+
})
46+
}
47+
}

0 commit comments

Comments
 (0)