@buntal/http 0.0.6 → 0.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/app/cookie.ts +7 -4
- package/index.ts +1 -0
- package/middlewares/auth/index.ts +95 -0
- package/middlewares/auth/jwt.ts +21 -0
- package/middlewares/cors.ts +39 -0
- package/middlewares/index.ts +3 -0
- package/middlewares/logger.ts +12 -0
- package/package.json +5 -2
- package/router/index.ts +1 -1
package/app/cookie.ts
CHANGED
|
@@ -28,10 +28,13 @@ export const Cookie = {
|
|
|
28
28
|
const cookies = req.headers.get('cookie')
|
|
29
29
|
if (!cookies) return {}
|
|
30
30
|
const cookieArray = cookies.split('; ')
|
|
31
|
-
return cookieArray.reduce(
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
31
|
+
return cookieArray.reduce(
|
|
32
|
+
(acc, cookie) => {
|
|
33
|
+
const [key, value] = cookie.split('=') as [string, string?]
|
|
34
|
+
return { ...acc, [key]: decodeURIComponent(value || '') }
|
|
35
|
+
},
|
|
36
|
+
{} as Record<string, string>
|
|
37
|
+
)
|
|
35
38
|
},
|
|
36
39
|
set: (
|
|
37
40
|
res: Res,
|
package/index.ts
CHANGED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { Cookie, Req, Res } from '../../app'
|
|
2
|
+
import type { AtomicHandler } from '../../handler'
|
|
3
|
+
import { jwt } from './jwt'
|
|
4
|
+
|
|
5
|
+
type Strategy = 'cookie' | 'header' | 'both'
|
|
6
|
+
|
|
7
|
+
type Options<T = unknown> = {
|
|
8
|
+
secret: string
|
|
9
|
+
strategy?: Strategy
|
|
10
|
+
cookie?: {
|
|
11
|
+
key: string
|
|
12
|
+
}
|
|
13
|
+
header?: {
|
|
14
|
+
key: string
|
|
15
|
+
}
|
|
16
|
+
onVerified?: (
|
|
17
|
+
req: Req<Record<string, string>, T>,
|
|
18
|
+
res: Res,
|
|
19
|
+
decoded: T
|
|
20
|
+
) => void | Response | Promise<void | Response>
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const getToken = (req: Req, strategy: Strategy, opts: Partial<Options>) => {
|
|
24
|
+
const cookieValue = Cookie.get(req, opts?.cookie?.key || 'access_token')
|
|
25
|
+
const headerValue = req.headers
|
|
26
|
+
.get(opts?.header?.key || 'Authorization')
|
|
27
|
+
?.replace(/^Bearer\ /, '')
|
|
28
|
+
switch (strategy) {
|
|
29
|
+
case 'cookie':
|
|
30
|
+
return cookieValue
|
|
31
|
+
case 'header':
|
|
32
|
+
return headerValue
|
|
33
|
+
case 'both':
|
|
34
|
+
return cookieValue || headerValue
|
|
35
|
+
default:
|
|
36
|
+
return null
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export const auth = <T = unknown>(
|
|
41
|
+
{
|
|
42
|
+
secret,
|
|
43
|
+
strategy = 'header',
|
|
44
|
+
cookie = {
|
|
45
|
+
key: 'access_token'
|
|
46
|
+
},
|
|
47
|
+
header = {
|
|
48
|
+
key: 'Authorization'
|
|
49
|
+
},
|
|
50
|
+
onVerified
|
|
51
|
+
}: Options<T> = {
|
|
52
|
+
secret: process.env.JWT_SECRET || process.env.SECRET || ''
|
|
53
|
+
}
|
|
54
|
+
): AtomicHandler<Record<string, string>, T> => {
|
|
55
|
+
return async (req, res) => {
|
|
56
|
+
let token: string | null | undefined
|
|
57
|
+
|
|
58
|
+
if (Array.isArray(strategy)) {
|
|
59
|
+
for (const strat of strategy) {
|
|
60
|
+
token = getToken(req, strat, {
|
|
61
|
+
cookie,
|
|
62
|
+
header
|
|
63
|
+
})
|
|
64
|
+
if (token) break
|
|
65
|
+
}
|
|
66
|
+
} else {
|
|
67
|
+
token = getToken(req, strategy, {
|
|
68
|
+
cookie,
|
|
69
|
+
header
|
|
70
|
+
})
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (!token) {
|
|
74
|
+
return res.status(401).json({
|
|
75
|
+
error: 'Unauthorized'
|
|
76
|
+
})
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
let decoded: T | null = null
|
|
80
|
+
try {
|
|
81
|
+
decoded = await jwt(secret).verify<T>(token)
|
|
82
|
+
} catch (error) {
|
|
83
|
+
return res.status(401).json({
|
|
84
|
+
error: 'Unauthorized'
|
|
85
|
+
})
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const resp = await onVerified?.(req, res, decoded)
|
|
89
|
+
if (resp instanceof Response) {
|
|
90
|
+
return resp
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export * from './jwt'
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { jwtVerify, SignJWT } from 'jose'
|
|
2
|
+
|
|
3
|
+
export const jwt = (secret: string) => ({
|
|
4
|
+
sign: async (
|
|
5
|
+
payload: Record<string, unknown>,
|
|
6
|
+
options: { expiresIn?: string | number | Date } = {}
|
|
7
|
+
) => {
|
|
8
|
+
return await new SignJWT(payload)
|
|
9
|
+
.setExpirationTime(options.expiresIn || '2h')
|
|
10
|
+
.setIssuedAt()
|
|
11
|
+
.setProtectedHeader({ alg: 'HS256' })
|
|
12
|
+
.sign(new TextEncoder().encode(secret))
|
|
13
|
+
},
|
|
14
|
+
verify: async <T = unknown>(token: string) => {
|
|
15
|
+
const { payload } = await jwtVerify<T>(
|
|
16
|
+
token,
|
|
17
|
+
new TextEncoder().encode(secret)
|
|
18
|
+
)
|
|
19
|
+
return payload
|
|
20
|
+
}
|
|
21
|
+
})
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { Req, Res } from '../app'
|
|
2
|
+
import type { AtomicHandler } from '../handler'
|
|
3
|
+
|
|
4
|
+
type Options = {
|
|
5
|
+
origin?: string | string[]
|
|
6
|
+
methods?: string | string[]
|
|
7
|
+
allowedHeaders?: string | string[]
|
|
8
|
+
exposedHeaders?: string | string[]
|
|
9
|
+
maxAge?: number
|
|
10
|
+
credentials?: boolean
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const cors = ({
|
|
14
|
+
origin = '*',
|
|
15
|
+
methods = 'GET, HEAD, PUT, PATCH, POST, DELETE',
|
|
16
|
+
allowedHeaders = 'Content-Type, Authorization, X-Requested-With',
|
|
17
|
+
exposedHeaders = '',
|
|
18
|
+
maxAge = 600,
|
|
19
|
+
credentials = true
|
|
20
|
+
}: Options = {}): AtomicHandler => {
|
|
21
|
+
return async (_: Req, res: Res) => {
|
|
22
|
+
res.headers({
|
|
23
|
+
'Access-Control-Allow-Origin': Array.isArray(origin)
|
|
24
|
+
? origin.join(', ')
|
|
25
|
+
: origin,
|
|
26
|
+
'Access-Control-Allow-Methods': Array.isArray(methods)
|
|
27
|
+
? methods.join(', ')
|
|
28
|
+
: methods,
|
|
29
|
+
'Access-Control-Allow-Headers': Array.isArray(allowedHeaders)
|
|
30
|
+
? allowedHeaders.join(', ')
|
|
31
|
+
: allowedHeaders,
|
|
32
|
+
'Access-Control-Expose-Headers': Array.isArray(exposedHeaders)
|
|
33
|
+
? exposedHeaders.join(', ')
|
|
34
|
+
: exposedHeaders,
|
|
35
|
+
'Access-Control-Max-Age': String(maxAge),
|
|
36
|
+
'Access-Control-Allow-Credentials': String(credentials)
|
|
37
|
+
})
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Req } from '../app'
|
|
2
|
+
import type { AtomicHandler } from '../handler'
|
|
3
|
+
|
|
4
|
+
export const logger = (): AtomicHandler => {
|
|
5
|
+
return async (req: Req) => {
|
|
6
|
+
const now = new Date()
|
|
7
|
+
const print = (num: number) => num.toString().padStart(2, '0')
|
|
8
|
+
console.log(
|
|
9
|
+
`[${print(now.getHours())}:${print(now.getMinutes())}:${print(now.getSeconds())}] > ${req.method} ${new URL(req.url).pathname}`
|
|
10
|
+
)
|
|
11
|
+
}
|
|
12
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@buntal/http",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.8",
|
|
4
4
|
"module": "index.ts",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"devDependencies": {
|
|
@@ -15,5 +15,8 @@
|
|
|
15
15
|
},
|
|
16
16
|
"description": "HTTP client framework for Bun",
|
|
17
17
|
"homepage": "https://buntaljs.org",
|
|
18
|
-
"license": "MIT"
|
|
18
|
+
"license": "MIT",
|
|
19
|
+
"dependencies": {
|
|
20
|
+
"jose": "^6.0.11"
|
|
21
|
+
}
|
|
19
22
|
}
|
package/router/index.ts
CHANGED