@kava/kava-api-core 1.0.0 → 1.0.2

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.
Files changed (133) hide show
  1. package/.github/workflows/publish.yml +40 -0
  2. package/dist/{auth.util.d.ts → auth/auth.d.ts} +1 -5
  3. package/dist/{auth.util.js → auth/auth.js} +38 -30
  4. package/dist/auth/auth.js.map +1 -0
  5. package/dist/auth/index.d.ts +1 -0
  6. package/dist/auth/index.js +2 -0
  7. package/dist/auth/index.js.map +1 -0
  8. package/dist/{context.util.js → context/context.js} +1 -1
  9. package/dist/context/context.js.map +1 -0
  10. package/dist/context/index.d.ts +1 -0
  11. package/dist/context/index.js +2 -0
  12. package/dist/context/index.js.map +1 -0
  13. package/dist/{controller.util.js → controller/controller.js} +1 -1
  14. package/dist/controller/controller.js.map +1 -0
  15. package/dist/controller/index.d.ts +1 -0
  16. package/dist/controller/index.js +2 -0
  17. package/dist/controller/index.js.map +1 -0
  18. package/dist/{conversion.util.js → conversion/conversion.js} +1 -1
  19. package/dist/conversion/conversion.js.map +1 -0
  20. package/dist/conversion/index.d.ts +1 -0
  21. package/dist/conversion/index.js +2 -0
  22. package/dist/conversion/index.js.map +1 -0
  23. package/dist/{db.util.d.ts → db/db.d.ts} +9 -5
  24. package/dist/{db.util.js → db/db.js} +40 -29
  25. package/dist/db/db.js.map +1 -0
  26. package/dist/db/index.d.ts +1 -0
  27. package/dist/db/index.js +2 -0
  28. package/dist/db/index.js.map +1 -0
  29. package/dist/index.d.ts +14 -13
  30. package/dist/index.js +14 -13
  31. package/dist/index.js.map +1 -1
  32. package/dist/logger/index.d.ts +1 -0
  33. package/dist/logger/index.js +2 -0
  34. package/dist/logger/index.js.map +1 -0
  35. package/dist/{logger.util.js → logger/logger.js} +16 -7
  36. package/dist/logger/logger.js.map +1 -0
  37. package/dist/mail/index.d.ts +1 -0
  38. package/dist/mail/index.js +2 -0
  39. package/dist/mail/index.js.map +1 -0
  40. package/dist/{mail.util.js → mail/mail.js} +1 -1
  41. package/dist/mail/mail.js.map +1 -0
  42. package/dist/middleware/index.d.ts +1 -0
  43. package/dist/middleware/index.js +2 -0
  44. package/dist/middleware/index.js.map +1 -0
  45. package/dist/{middleware.util.js → middleware/middleware.js} +1 -1
  46. package/dist/middleware/middleware.js.map +1 -0
  47. package/dist/model/index.d.ts +1 -0
  48. package/dist/model/index.js +2 -0
  49. package/dist/model/index.js.map +1 -0
  50. package/dist/{model.util.js → model/model.js} +1 -1
  51. package/dist/model/model.js.map +1 -0
  52. package/dist/permission/index.d.ts +1 -0
  53. package/dist/permission/index.js +2 -0
  54. package/dist/permission/index.js.map +1 -0
  55. package/dist/{permission.util.js → permission/permission.js} +1 -1
  56. package/dist/permission/permission.js.map +1 -0
  57. package/dist/registry/index.d.ts +1 -0
  58. package/dist/registry/index.js +2 -0
  59. package/dist/registry/index.js.map +1 -0
  60. package/dist/registry/registry.d.ts +28 -0
  61. package/dist/registry/registry.js +19 -0
  62. package/dist/registry/registry.js.map +1 -0
  63. package/dist/route/index.d.ts +1 -0
  64. package/dist/route/index.js +2 -0
  65. package/dist/route/index.js.map +1 -0
  66. package/dist/{route.util.js → route/route.js} +1 -1
  67. package/dist/route/route.js.map +1 -0
  68. package/dist/storage/index.d.ts +1 -0
  69. package/dist/storage/index.js +2 -0
  70. package/dist/storage/index.js.map +1 -0
  71. package/dist/{storage.util.js → storage/storage.js} +2 -2
  72. package/dist/storage/storage.js.map +1 -0
  73. package/dist/validation/index.d.ts +1 -0
  74. package/dist/validation/index.js +2 -0
  75. package/dist/validation/index.js.map +1 -0
  76. package/dist/{validation.util.js → validation/validation.js} +1 -1
  77. package/dist/validation/validation.js.map +1 -0
  78. package/package.json +2 -2
  79. package/src/{auth.util.ts → auth/auth.ts} +255 -241
  80. package/src/auth/index.ts +1 -0
  81. package/src/{context.util.ts → context/context.ts} +17 -17
  82. package/src/context/index.ts +1 -0
  83. package/src/{controller.util.ts → controller/controller.ts} +236 -236
  84. package/src/controller/index.ts +1 -0
  85. package/src/{conversion.util.ts → conversion/conversion.ts} +64 -64
  86. package/src/conversion/index.ts +1 -0
  87. package/src/{db.util.ts → db/db.ts} +420 -405
  88. package/src/db/index.ts +1 -0
  89. package/src/index.ts +14 -13
  90. package/src/logger/index.ts +1 -0
  91. package/src/{logger.util.ts → logger/logger.ts} +176 -169
  92. package/src/mail/index.ts +1 -0
  93. package/src/{mail.util.ts → mail/mail.ts} +85 -85
  94. package/src/middleware/index.ts +1 -0
  95. package/src/{middleware.util.ts → middleware/middleware.ts} +288 -288
  96. package/src/model/index.ts +1 -0
  97. package/src/{model.util.ts → model/model.ts} +2210 -2210
  98. package/src/permission/index.ts +1 -0
  99. package/src/{permission.util.ts → permission/permission.ts} +136 -136
  100. package/src/registry/index.ts +1 -0
  101. package/src/registry/registry.ts +37 -0
  102. package/src/route/index.ts +1 -0
  103. package/src/{route.util.ts → route/route.ts} +11 -11
  104. package/src/storage/index.ts +1 -0
  105. package/src/{storage.util.ts → storage/storage.ts} +101 -101
  106. package/src/validation/index.ts +1 -0
  107. package/src/{validation.util.ts → validation/validation.ts} +338 -338
  108. package/tsconfig.json +1 -1
  109. package/bun.lock +0 -160
  110. package/dist/auth.util.js.map +0 -1
  111. package/dist/context.util.js.map +0 -1
  112. package/dist/controller.util.js.map +0 -1
  113. package/dist/conversion.util.js.map +0 -1
  114. package/dist/db.util.js.map +0 -1
  115. package/dist/logger.util.js.map +0 -1
  116. package/dist/mail.util.js.map +0 -1
  117. package/dist/middleware.util.js.map +0 -1
  118. package/dist/model.util.js.map +0 -1
  119. package/dist/permission.util.js.map +0 -1
  120. package/dist/route.util.js.map +0 -1
  121. package/dist/storage.util.js.map +0 -1
  122. package/dist/validation.util.js.map +0 -1
  123. /package/dist/{context.util.d.ts → context/context.d.ts} +0 -0
  124. /package/dist/{controller.util.d.ts → controller/controller.d.ts} +0 -0
  125. /package/dist/{conversion.util.d.ts → conversion/conversion.d.ts} +0 -0
  126. /package/dist/{logger.util.d.ts → logger/logger.d.ts} +0 -0
  127. /package/dist/{mail.util.d.ts → mail/mail.d.ts} +0 -0
  128. /package/dist/{middleware.util.d.ts → middleware/middleware.d.ts} +0 -0
  129. /package/dist/{model.util.d.ts → model/model.d.ts} +0 -0
  130. /package/dist/{permission.util.d.ts → permission/permission.d.ts} +0 -0
  131. /package/dist/{route.util.d.ts → route/route.d.ts} +0 -0
  132. /package/dist/{storage.util.d.ts → storage/storage.d.ts} +0 -0
  133. /package/dist/{validation.util.d.ts → validation/validation.d.ts} +0 -0
@@ -1,242 +1,256 @@
1
- import crypto from 'crypto'
2
- import bcrypt from "bcrypt";
3
- import { db } from '@utils'
4
-
5
-
6
-
7
- // =====================================>
8
- // ## Auth: User Access Token
9
- // =====================================>
10
- const TOKEN_PLAIN_LENGTH = 20
11
- const AUTH_PERMISSION = process.env.AUTH_CACHE === "true"
12
- // const AUTH_CACHE = process.env.AUTH_CACHE === "true"
13
- // const AUTH_CACHE_TTL = Number(process.env.AUTH_CACHE_TTL || 600)
14
-
15
-
16
-
17
- export const auth = {
18
-
19
- // =====================================>
20
- // ## Auth: create access token with user id
21
- // =====================================>
22
- async createAccessToken(userId: number, req: Request, permission: boolean = true) {
23
- const plain = crypto.randomBytes(TOKEN_PLAIN_LENGTH).toString("hex")
24
- const hash = await bcrypt.hash(plain, 10)
25
- const agent = generateAgentId(req)
26
-
27
- let permissions: string[] = []
28
- if (AUTH_PERMISSION && permission) {
29
- permissions = await getUserPermissions(userId)
30
- }
31
-
32
- const [row] = await db("user_access_tokens").insert({
33
- user_id : userId,
34
- token : hash,
35
- agent : agent,
36
- permissions : JSON.stringify(permissions),
37
- created_at : new Date(),
38
- }).returning(["id"])
39
-
40
- return {
41
- token : `${row.id}|${plain}`,
42
- tokenId : row.id,
43
- }
44
- },
45
-
46
-
47
-
48
- // =====================================>
49
- // ## Auth: delete access token with user id
50
- // =====================================>
51
- async revokeAccessToken(id: number) {
52
- return db.table('user_access_tokens').where("id", id).delete()
53
- },
54
-
55
-
56
-
57
- // =====================================>
58
- // ## Auth: verify access token
59
- // =====================================>
60
- async verifyAccessToken(token: string, req?: Request) {
61
- if (!token.includes("|")) return null
62
-
63
- const [tokenId, plain] = token.split("|", 2)
64
- const agent = req ? generateAgentId(req) : ""
65
- const ip = req ? getRequestIp(req) : ""
66
-
67
- const cacheKey = `auth:token:${tokenId}`
68
-
69
- // if (AUTH_CACHE) {
70
- // const cached = await redis.get(cacheKey)
71
-
72
- // if (cached) {
73
- // const session = JSON.parse(cached)
74
-
75
- // if (session.agent !== agent) return null
76
-
77
- // return session
78
- // }
79
- // }
80
-
81
- const tokenRecord = await db("user_access_tokens").where("id", tokenId).first()
82
-
83
- if (!tokenRecord) return null
84
- if (tokenRecord.agent !== agent) return null
85
-
86
- const valid = await bcrypt.compare(plain, tokenRecord.token)
87
- if (!valid) return null
88
-
89
- await db("user_access_tokens").where("id", tokenRecord.id).update({ last_used_at: new Date(), last_used_ip: ip })
90
-
91
- const user = await db("users").where("id", tokenRecord.user_id).first()
92
-
93
- // if (AUTH_CACHE) {
94
- // await redis.setex(
95
- // cacheKey,
96
- // AUTH_CACHE_TTL,
97
- // JSON.stringify({
98
- // user : user,
99
- // agent : tokenRecord.agent,
100
- // permissions : tokenRecord.permission,
101
- // })
102
- // )
103
- // }
104
-
105
- return { user, token: tokenRecord, permissions: tokenRecord.permission }
106
- },
107
-
108
-
109
-
110
- // =====================================>
111
- // ## Auth: create user mail token
112
- // =====================================>
113
- async createUserMailToken(userId: number) {
114
- const token = Math.floor(100000 + Math.random() * 900000).toString()
115
- const hash = crypto.createHash('sha256').update(token).digest('hex')
116
-
117
- const trx = await db.transaction()
118
-
119
- await trx.table('user_mail_tokens').insert({
120
- user_id : userId,
121
- token : hash,
122
- created_at : new Date(),
123
- })
124
-
125
- const record = await trx.table('user_mail_tokens').orderBy('id', 'desc').first()
126
-
127
- await trx.commit()
128
-
129
- return {
130
- token : token,
131
- tokenId : record.id
132
- }
133
- },
134
-
135
-
136
-
137
- // =====================================>
138
- // ## Auth: Verify user mail token
139
- // =====================================>
140
- async verifyUserMailToken(userId: number, token: string) {
141
- const hashedToken = crypto.createHash("sha256").update(token).digest("hex");
142
-
143
- const record = await db.table("user_mail_tokens")
144
- .where("user_id", userId)
145
- .whereNull("used_at")
146
- .orderBy("id", "desc")
147
- .first();
148
-
149
- if (!record) return false
150
-
151
- if (record.token !== hashedToken) return false;
152
-
153
- const createdAt = new Date(record.created_at);
154
- const now = new Date();
155
- const diffMinutes = (now.getTime() - createdAt.getTime()) / (1000 * 60);
156
-
157
- if (diffMinutes > 10) return false;
158
-
159
- return true;
160
- },
161
-
162
-
163
-
164
- // =====================================>
165
- // ## Auth: list user sessions
166
- // =====================================>
167
- async listUserSessions(userId: number, currentTokenId?: number) {
168
- const rows = await db("user_access_tokens").select(["id", "agent", "created_at", "last_used_at", "last_used_ip","expired_at"]).where("user_id", userId).orderBy("last_used_at", "desc")
169
-
170
- return rows.map(r => ({
171
- ...r,
172
- is_active : r.revoked_at === null,
173
- is_current : r.id === currentTokenId,
174
- }))
175
- },
176
-
177
-
178
-
179
- // =====================================>
180
- // ## Auth: revalidate user permission
181
- // =====================================>
182
- revalidateUserPermissions: revalidateUserPermissions,
183
- revalidateUserPermissionsByRole: revalidateUserPermissionsByRole,
184
- }
185
-
186
-
187
-
188
- function generateAgentId(req: Request) {
189
- const ua = req.headers.get("user-agent") ?? ""
190
- const acc = req.headers.get("accept") ?? ""
191
-
192
- return crypto.createHash("sha256").update(ua + acc).digest("hex")
193
- }
194
-
195
-
196
- function getRequestIp(req: Request) {
197
- return (req.headers.get("x-forwarded-for")?.split(",")[0]?.trim() || req.headers.get("x-real-ip") || "unknown")
198
- }
199
-
200
-
201
- async function getUserPermissions(userId: number): Promise<string[]> {
202
- const roleIds = await db("user_roles").where("user_id", userId).pluck("role_id")
203
-
204
- if (roleIds.length === 0) return []
205
-
206
- const rows = await db("permissions").whereIn("role_id", roleIds).pluck("permissions")
207
-
208
- return Array.from(
209
- new Set(
210
- rows.flatMap(p => p ?? [])
211
- )
212
- )
213
- }
214
-
215
-
216
- async function revalidateUserPermissions(userId: number) {
217
- const permissions = await getUserPermissions(userId)
218
-
219
- const tokenIds = await db("user_access_tokens").where("user_id", userId).pluck("id")
220
-
221
- if (tokenIds.length === 0) return
222
-
223
- await db("user_access_tokens").whereIn("id", tokenIds).update({
224
- permissions : JSON.stringify(permissions),
225
- updated_at : new Date(),
226
- })
227
-
228
- // if (AUTH_CACHE) {
229
- // await Promise.all(
230
- // tokenIds.map(id => redis.del(`auth:token:${id}`))
231
- // )
232
- // }
233
- }
234
-
235
-
236
- async function revalidateUserPermissionsByRole(roleId: number) {
237
- const userIds = await db("user_roles").where("role_id", roleId).pluck("user_id")
238
-
239
- // for (const userId of userIds) {
240
- // await queue.add("auth:revalidate-permission", { userId })
241
- // }
1
+ import crypto from 'crypto'
2
+ import bcrypt from "bcrypt";
3
+ import { db } from '@utils'
4
+ import { registry } from '@utils/registry'
5
+
6
+
7
+
8
+ // =====================================>
9
+ // ## Auth: User Access Token
10
+ // =====================================>
11
+ const TOKEN_PLAIN_LENGTH = 20
12
+ const AUTH_PERMISSION = process.env.AUTH_CACHE === "true"
13
+ const AUTH_CACHE = process.env.AUTH_CACHE === "true"
14
+ const AUTH_CACHE_TTL = Number(process.env.AUTH_CACHE_TTL || 600)
15
+
16
+
17
+
18
+ export const auth = {
19
+
20
+ // =====================================>
21
+ // ## Auth: create access token with user id
22
+ // =====================================>
23
+ async createAccessToken(userId: number, req: Request, permission: boolean = true) {
24
+ const plain = crypto.randomBytes(TOKEN_PLAIN_LENGTH).toString("hex")
25
+ const hash = await bcrypt.hash(plain, 10)
26
+ const agent = generateAgentId(req)
27
+
28
+ let permissions: string[] = []
29
+ if (AUTH_PERMISSION && permission) {
30
+ permissions = await getUserPermissions(userId)
31
+ }
32
+
33
+ const [row] = await db("user_access_tokens").insert({
34
+ user_id : userId,
35
+ token : hash,
36
+ agent : agent,
37
+ permissions : JSON.stringify(permissions),
38
+ created_at : new Date(),
39
+ }).returning(["id"])
40
+
41
+ return {
42
+ token : `${row.id}|${plain}`,
43
+ tokenId : row.id,
44
+ }
45
+ },
46
+
47
+
48
+
49
+ // =====================================>
50
+ // ## Auth: delete access token with user id
51
+ // =====================================>
52
+ async revokeAccessToken(id: number) {
53
+ return db.table('user_access_tokens').where("id", id).delete()
54
+ },
55
+
56
+
57
+
58
+ // =====================================>
59
+ // ## Auth: verify access token
60
+ // =====================================>
61
+ async verifyAccessToken(token: string, req?: Request) {
62
+ if (!token.includes("|")) return null
63
+
64
+ const [tokenId, plain] = token.split("|", 2)
65
+ const agent = req ? generateAgentId(req) : ""
66
+ const ip = req ? getRequestIp(req) : ""
67
+
68
+ const cacheKey = `auth:token:${tokenId}`
69
+
70
+ if (AUTH_CACHE) {
71
+ const redis = registry.get('redis')
72
+
73
+ if (redis) {
74
+ const cached = await redis.get(cacheKey)
75
+
76
+ if (cached) {
77
+ const session = JSON.parse(cached)
78
+
79
+ if (session.agent !== agent) return null
80
+
81
+ return session
82
+ }
83
+ }
84
+ }
85
+
86
+ const tokenRecord = await db("user_access_tokens").where("id", tokenId).first()
87
+
88
+ if (!tokenRecord) return null
89
+ if (tokenRecord.agent !== agent) return null
90
+
91
+ const valid = await bcrypt.compare(plain, tokenRecord.token)
92
+ if (!valid) return null
93
+
94
+ await db("user_access_tokens").where("id", tokenRecord.id).update({ last_used_at: new Date(), last_used_ip: ip })
95
+
96
+ const user = await db("users").where("id", tokenRecord.user_id).first()
97
+
98
+ if (AUTH_CACHE) {
99
+ const redis = registry.get('redis')
100
+ if (redis) {
101
+ await redis.setex(
102
+ cacheKey,
103
+ AUTH_CACHE_TTL,
104
+ JSON.stringify({
105
+ user : user,
106
+ agent : tokenRecord.agent,
107
+ permissions : tokenRecord.permission,
108
+ })
109
+ )
110
+ }
111
+ }
112
+
113
+ return { user, token: tokenRecord, permissions: tokenRecord.permission }
114
+ },
115
+
116
+
117
+
118
+ // =====================================>
119
+ // ## Auth: create user mail token
120
+ // =====================================>
121
+ async createUserMailToken(userId: number) {
122
+ const token = Math.floor(100000 + Math.random() * 900000).toString()
123
+ const hash = crypto.createHash('sha256').update(token).digest('hex')
124
+
125
+ const trx = await db.transaction()
126
+
127
+ await trx.table('user_mail_tokens').insert({
128
+ user_id : userId,
129
+ token : hash,
130
+ created_at : new Date(),
131
+ })
132
+
133
+ const record = await trx.table('user_mail_tokens').orderBy('id', 'desc').first()
134
+
135
+ await trx.commit()
136
+
137
+ return {
138
+ token : token,
139
+ tokenId : record.id
140
+ }
141
+ },
142
+
143
+
144
+
145
+ // =====================================>
146
+ // ## Auth: Verify user mail token
147
+ // =====================================>
148
+ async verifyUserMailToken(userId: number, token: string) {
149
+ const hashedToken = crypto.createHash("sha256").update(token).digest("hex");
150
+
151
+ const record = await db.table("user_mail_tokens")
152
+ .where("user_id", userId)
153
+ .whereNull("used_at")
154
+ .orderBy("id", "desc")
155
+ .first();
156
+
157
+ if (!record) return false
158
+
159
+ if (record.token !== hashedToken) return false;
160
+
161
+ const createdAt = new Date(record.created_at);
162
+ const now = new Date();
163
+ const diffMinutes = (now.getTime() - createdAt.getTime()) / (1000 * 60);
164
+
165
+ if (diffMinutes > 10) return false;
166
+
167
+ return true;
168
+ },
169
+
170
+
171
+
172
+ // =====================================>
173
+ // ## Auth: list user sessions
174
+ // =====================================>
175
+ async listUserSessions(userId: number, currentTokenId?: number) {
176
+ const rows = await db("user_access_tokens").select(["id", "agent", "created_at", "last_used_at", "last_used_ip","expired_at"]).where("user_id", userId).orderBy("last_used_at", "desc")
177
+
178
+ return rows.map(r => ({
179
+ ...r,
180
+ is_active : r.revoked_at === null,
181
+ is_current : r.id === currentTokenId,
182
+ }))
183
+ },
184
+
185
+
186
+
187
+ // =====================================>
188
+ // ## Auth: revalidate user permission
189
+ // =====================================>
190
+ revalidateUserPermissions: revalidateUserPermissions,
191
+ revalidateUserPermissionsByRole: revalidateUserPermissionsByRole,
192
+ }
193
+
194
+
195
+
196
+ function generateAgentId(req: Request) {
197
+ const ua = req.headers.get("user-agent") ?? ""
198
+ const acc = req.headers.get("accept") ?? ""
199
+
200
+ return crypto.createHash("sha256").update(ua + acc).digest("hex")
201
+ }
202
+
203
+
204
+ function getRequestIp(req: Request) {
205
+ return (req.headers.get("x-forwarded-for")?.split(",")[0]?.trim() || req.headers.get("x-real-ip") || "unknown")
206
+ }
207
+
208
+
209
+ async function getUserPermissions(userId: number): Promise<string[]> {
210
+ const roleIds = await db("user_roles").where("user_id", userId).pluck("role_id")
211
+
212
+ if (roleIds.length === 0) return []
213
+
214
+ const rows = await db("permissions").whereIn("role_id", roleIds).pluck("permissions")
215
+
216
+ return Array.from(
217
+ new Set(
218
+ rows.flatMap(p => p ?? [])
219
+ )
220
+ )
221
+ }
222
+
223
+
224
+ async function revalidateUserPermissions(userId: number) {
225
+ const permissions = await getUserPermissions(userId)
226
+
227
+ const tokenIds = await db("user_access_tokens").where("user_id", userId).pluck("id")
228
+
229
+ if (tokenIds.length === 0) return
230
+
231
+ await db("user_access_tokens").whereIn("id", tokenIds).update({
232
+ permissions : JSON.stringify(permissions),
233
+ updated_at : new Date(),
234
+ })
235
+
236
+ if (AUTH_CACHE) {
237
+ const redis = registry.get('redis')
238
+ if (redis) {
239
+ await Promise.all(
240
+ tokenIds.map(id => redis.del(`auth:token:${id}`))
241
+ )
242
+ }
243
+ }
244
+ }
245
+
246
+
247
+ async function revalidateUserPermissionsByRole(roleId: number) {
248
+ const userIds = await db("user_roles").where("role_id", roleId).pluck("user_id")
249
+
250
+ const queue = registry.get('queue')
251
+ if (queue) {
252
+ for (const userId of userIds) {
253
+ await queue.add("auth:revalidate-permission", { userId })
254
+ }
255
+ }
242
256
  }
@@ -0,0 +1 @@
1
+ export * from "./auth";
@@ -1,17 +1,17 @@
1
- import { AsyncLocalStorage } from 'node:async_hooks'
2
-
3
- export type AppContext = {
4
- user_id?: number,
5
- }
6
-
7
- const storage = new AsyncLocalStorage<AppContext>()
8
-
9
- export const context = {
10
- run<T>(ctx: AppContext, fn: () => T) {
11
- return storage.run(ctx, fn)
12
- },
13
-
14
- get<K extends keyof AppContext>(key: K): AppContext[K] {
15
- return storage.getStore()?.[key]
16
- },
17
- }
1
+ import { AsyncLocalStorage } from 'node:async_hooks'
2
+
3
+ export type AppContext = {
4
+ user_id?: number,
5
+ }
6
+
7
+ const storage = new AsyncLocalStorage<AppContext>()
8
+
9
+ export const context = {
10
+ run<T>(ctx: AppContext, fn: () => T) {
11
+ return storage.run(ctx, fn)
12
+ },
13
+
14
+ get<K extends keyof AppContext>(key: K): AppContext[K] {
15
+ return storage.getStore()?.[key]
16
+ },
17
+ }
@@ -0,0 +1 @@
1
+ export * from "./context";