@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 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((acc, cookie) => {
32
- const [key, value] = cookie.split('=') as [string, string?]
33
- return { ...acc, [key]: decodeURIComponent(value || '') }
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
@@ -184,3 +184,4 @@ export class Http {
184
184
  export * from './app'
185
185
  export * from './router'
186
186
  export * from './handler'
187
+ export * from './middlewares'
@@ -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,3 @@
1
+ export * from './auth'
2
+ export * from './cors'
3
+ export * from './logger'
@@ -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.6",
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
@@ -1,5 +1,5 @@
1
1
  export const buildRouter = (dir: string) =>
2
2
  new Bun.FileSystemRouter({
3
3
  style: 'nextjs',
4
- dir: dir
4
+ dir,
5
5
  })