sa2kit 1.0.0
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/LICENSE +22 -0
- package/README.md +298 -0
- package/dist/AliyunOSSProvider-7JLMJDXK.js +15 -0
- package/dist/AliyunOSSProvider-7JLMJDXK.js.map +1 -0
- package/dist/AliyunOSSProvider-GQMSDJGZ.mjs +6 -0
- package/dist/AliyunOSSProvider-GQMSDJGZ.mjs.map +1 -0
- package/dist/LocalStorageProvider-FVLLHBHO.mjs +6 -0
- package/dist/LocalStorageProvider-FVLLHBHO.mjs.map +1 -0
- package/dist/LocalStorageProvider-NBNHHWLY.js +15 -0
- package/dist/LocalStorageProvider-NBNHHWLY.js.map +1 -0
- package/dist/analytics/index.d.mts +1084 -0
- package/dist/analytics/index.d.ts +1084 -0
- package/dist/analytics/index.js +2595 -0
- package/dist/analytics/index.js.map +1 -0
- package/dist/analytics/index.mjs +2518 -0
- package/dist/analytics/index.mjs.map +1 -0
- package/dist/analytics/server/index.d.mts +499 -0
- package/dist/analytics/server/index.d.ts +499 -0
- package/dist/analytics/server/index.js +529 -0
- package/dist/analytics/server/index.js.map +1 -0
- package/dist/analytics/server/index.mjs +525 -0
- package/dist/analytics/server/index.mjs.map +1 -0
- package/dist/auth/client/index.d.mts +104 -0
- package/dist/auth/client/index.d.ts +104 -0
- package/dist/auth/client/index.js +21 -0
- package/dist/auth/client/index.js.map +1 -0
- package/dist/auth/client/index.mjs +4 -0
- package/dist/auth/client/index.mjs.map +1 -0
- package/dist/auth/components/index.d.mts +82 -0
- package/dist/auth/components/index.d.ts +82 -0
- package/dist/auth/components/index.js +93 -0
- package/dist/auth/components/index.js.map +1 -0
- package/dist/auth/components/index.mjs +86 -0
- package/dist/auth/components/index.mjs.map +1 -0
- package/dist/auth/hooks/index.d.mts +2 -0
- package/dist/auth/hooks/index.d.ts +2 -0
- package/dist/auth/hooks/index.js +17 -0
- package/dist/auth/hooks/index.js.map +1 -0
- package/dist/auth/hooks/index.mjs +4 -0
- package/dist/auth/hooks/index.mjs.map +1 -0
- package/dist/auth/index.d.mts +15 -0
- package/dist/auth/index.d.ts +15 -0
- package/dist/auth/index.js +110 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/auth/index.mjs +9 -0
- package/dist/auth/index.mjs.map +1 -0
- package/dist/auth/middleware/index.d.mts +75 -0
- package/dist/auth/middleware/index.d.ts +75 -0
- package/dist/auth/middleware/index.js +15 -0
- package/dist/auth/middleware/index.js.map +1 -0
- package/dist/auth/middleware/index.mjs +6 -0
- package/dist/auth/middleware/index.mjs.map +1 -0
- package/dist/auth/routes/index.d.mts +163 -0
- package/dist/auth/routes/index.d.ts +163 -0
- package/dist/auth/routes/index.js +27 -0
- package/dist/auth/routes/index.js.map +1 -0
- package/dist/auth/routes/index.mjs +6 -0
- package/dist/auth/routes/index.mjs.map +1 -0
- package/dist/auth/schema/index.d.mts +789 -0
- package/dist/auth/schema/index.d.ts +789 -0
- package/dist/auth/schema/index.js +41 -0
- package/dist/auth/schema/index.js.map +1 -0
- package/dist/auth/schema/index.mjs +4 -0
- package/dist/auth/schema/index.mjs.map +1 -0
- package/dist/auth/services/index.d.mts +47 -0
- package/dist/auth/services/index.d.ts +47 -0
- package/dist/auth/services/index.js +34 -0
- package/dist/auth/services/index.js.map +1 -0
- package/dist/auth/services/index.mjs +5 -0
- package/dist/auth/services/index.mjs.map +1 -0
- package/dist/chunk-3RFBUDRA.js +507 -0
- package/dist/chunk-3RFBUDRA.js.map +1 -0
- package/dist/chunk-3XG5OHFD.mjs +37 -0
- package/dist/chunk-3XG5OHFD.mjs.map +1 -0
- package/dist/chunk-6BL3AZGD.js +285 -0
- package/dist/chunk-6BL3AZGD.js.map +1 -0
- package/dist/chunk-6FNUWAIV.js +394 -0
- package/dist/chunk-6FNUWAIV.js.map +1 -0
- package/dist/chunk-6PRFP5EG.js +171 -0
- package/dist/chunk-6PRFP5EG.js.map +1 -0
- package/dist/chunk-6VHWOPRR.mjs +90 -0
- package/dist/chunk-6VHWOPRR.mjs.map +1 -0
- package/dist/chunk-AIKEVVDR.mjs +122 -0
- package/dist/chunk-AIKEVVDR.mjs.map +1 -0
- package/dist/chunk-APY57REU.js +300 -0
- package/dist/chunk-APY57REU.js.map +1 -0
- package/dist/chunk-BJTO5JO5.mjs +10 -0
- package/dist/chunk-BJTO5JO5.mjs.map +1 -0
- package/dist/chunk-C64RY2OW.mjs +295 -0
- package/dist/chunk-C64RY2OW.mjs.map +1 -0
- package/dist/chunk-DGUM43GV.js +12 -0
- package/dist/chunk-DGUM43GV.js.map +1 -0
- package/dist/chunk-FV3FNHQY.js +92 -0
- package/dist/chunk-FV3FNHQY.js.map +1 -0
- package/dist/chunk-GSTLV3MB.mjs +316 -0
- package/dist/chunk-GSTLV3MB.mjs.map +1 -0
- package/dist/chunk-HEMA7SWK.mjs +212 -0
- package/dist/chunk-HEMA7SWK.mjs.map +1 -0
- package/dist/chunk-HWJ34NL6.js +43 -0
- package/dist/chunk-HWJ34NL6.js.map +1 -0
- package/dist/chunk-HXFFYNIF.mjs +385 -0
- package/dist/chunk-HXFFYNIF.mjs.map +1 -0
- package/dist/chunk-KGRQNEIR.mjs +183 -0
- package/dist/chunk-KGRQNEIR.mjs.map +1 -0
- package/dist/chunk-KH6RQ4J5.js +28 -0
- package/dist/chunk-KH6RQ4J5.js.map +1 -0
- package/dist/chunk-KQGP6BTS.mjs +165 -0
- package/dist/chunk-KQGP6BTS.mjs.map +1 -0
- package/dist/chunk-NMF4ANIC.js +365 -0
- package/dist/chunk-NMF4ANIC.js.map +1 -0
- package/dist/chunk-O26VCNS3.js +216 -0
- package/dist/chunk-O26VCNS3.js.map +1 -0
- package/dist/chunk-OLHGZXN3.mjs +86 -0
- package/dist/chunk-OLHGZXN3.mjs.map +1 -0
- package/dist/chunk-QU5OT4DF.js +88 -0
- package/dist/chunk-QU5OT4DF.js.map +1 -0
- package/dist/chunk-RCNNVNLT.mjs +356 -0
- package/dist/chunk-RCNNVNLT.mjs.map +1 -0
- package/dist/chunk-ROEYW4A7.js +186 -0
- package/dist/chunk-ROEYW4A7.js.map +1 -0
- package/dist/chunk-SVWQN2LR.js +131 -0
- package/dist/chunk-SVWQN2LR.js.map +1 -0
- package/dist/chunk-TKCYPDWU.js +338 -0
- package/dist/chunk-TKCYPDWU.js.map +1 -0
- package/dist/chunk-U2L6V7KD.mjs +273 -0
- package/dist/chunk-U2L6V7KD.mjs.map +1 -0
- package/dist/chunk-YVBU7QDJ.mjs +505 -0
- package/dist/chunk-YVBU7QDJ.mjs.map +1 -0
- package/dist/chunk-ZGVB35L2.mjs +25 -0
- package/dist/chunk-ZGVB35L2.mjs.map +1 -0
- package/dist/config/index.d.mts +64 -0
- package/dist/config/index.d.ts +64 -0
- package/dist/config/index.js +136 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/index.mjs +128 -0
- package/dist/config/index.mjs.map +1 -0
- package/dist/drizzle-auth-service-Bxlovhv8.d.ts +145 -0
- package/dist/drizzle-auth-service-DZY2F1sv.d.mts +145 -0
- package/dist/enums-Dume-V5Y.d.mts +16 -0
- package/dist/enums-Dume-V5Y.d.ts +16 -0
- package/dist/i18n/index.d.mts +416 -0
- package/dist/i18n/index.d.ts +416 -0
- package/dist/i18n/index.js +671 -0
- package/dist/i18n/index.js.map +1 -0
- package/dist/i18n/index.mjs +650 -0
- package/dist/i18n/index.mjs.map +1 -0
- package/dist/index-8VoHap_4.d.mts +105 -0
- package/dist/index-8VoHap_4.d.ts +105 -0
- package/dist/index.d.mts +4 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +84 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +7 -0
- package/dist/index.mjs.map +1 -0
- package/dist/logger/index.d.mts +125 -0
- package/dist/logger/index.d.ts +125 -0
- package/dist/logger/index.js +29 -0
- package/dist/logger/index.js.map +1 -0
- package/dist/logger/index.mjs +4 -0
- package/dist/logger/index.mjs.map +1 -0
- package/dist/request/index.d.mts +51 -0
- package/dist/request/index.d.ts +51 -0
- package/dist/request/index.js +85 -0
- package/dist/request/index.js.map +1 -0
- package/dist/request/index.mjs +82 -0
- package/dist/request/index.mjs.map +1 -0
- package/dist/storage/index.d.mts +74 -0
- package/dist/storage/index.d.ts +74 -0
- package/dist/storage/index.js +46 -0
- package/dist/storage/index.js.map +1 -0
- package/dist/storage/index.mjs +5 -0
- package/dist/storage/index.mjs.map +1 -0
- package/dist/types-BINlP9MK.d.mts +286 -0
- package/dist/types-BINlP9MK.d.ts +286 -0
- package/dist/types-BaZccpvk.d.mts +48 -0
- package/dist/types-BaZccpvk.d.ts +48 -0
- package/dist/types-CbTsi9CZ.d.mts +31 -0
- package/dist/types-CbTsi9CZ.d.ts +31 -0
- package/dist/types-CoGG1rNV.d.mts +258 -0
- package/dist/types-CoGG1rNV.d.ts +258 -0
- package/dist/types-DAxQ1MeY.d.ts +70 -0
- package/dist/types-DT8LVCvE.d.mts +70 -0
- package/dist/types-DW9qar-w.d.mts +52 -0
- package/dist/types-DW9qar-w.d.ts +52 -0
- package/dist/universalExport/index.d.mts +235 -0
- package/dist/universalExport/index.d.ts +235 -0
- package/dist/universalExport/index.js +621 -0
- package/dist/universalExport/index.js.map +1 -0
- package/dist/universalExport/index.mjs +580 -0
- package/dist/universalExport/index.mjs.map +1 -0
- package/dist/universalExport/server/index.d.mts +429 -0
- package/dist/universalExport/server/index.d.ts +429 -0
- package/dist/universalExport/server/index.js +263 -0
- package/dist/universalExport/server/index.js.map +1 -0
- package/dist/universalExport/server/index.mjs +242 -0
- package/dist/universalExport/server/index.mjs.map +1 -0
- package/dist/universalFile/index.d.mts +310 -0
- package/dist/universalFile/index.d.ts +310 -0
- package/dist/universalFile/index.js +811 -0
- package/dist/universalFile/index.js.map +1 -0
- package/dist/universalFile/index.mjs +736 -0
- package/dist/universalFile/index.mjs.map +1 -0
- package/dist/universalFile/server/index.d.mts +2428 -0
- package/dist/universalFile/server/index.d.ts +2428 -0
- package/dist/universalFile/server/index.js +4578 -0
- package/dist/universalFile/server/index.js.map +1 -0
- package/dist/universalFile/server/index.mjs +4518 -0
- package/dist/universalFile/server/index.mjs.map +1 -0
- package/dist/useElectronStorage-Dj0rcorG.d.mts +65 -0
- package/dist/useElectronStorage-DwnNfIhl.d.ts +65 -0
- package/dist/utils/index.d.mts +188 -0
- package/dist/utils/index.d.ts +188 -0
- package/dist/utils/index.js +42 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/index.mjs +5 -0
- package/dist/utils/index.mjs.map +1 -0
- package/package.json +220 -0
- package/tailwind.animations.js +34 -0
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var chunkSVWQN2LR_js = require('./chunk-SVWQN2LR.js');
|
|
4
|
+
var drizzleOrm = require('drizzle-orm');
|
|
5
|
+
var crypto = require('crypto');
|
|
6
|
+
var bcrypt = require('bcryptjs');
|
|
7
|
+
var jwt = require('jsonwebtoken');
|
|
8
|
+
|
|
9
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
10
|
+
|
|
11
|
+
var bcrypt__default = /*#__PURE__*/_interopDefault(bcrypt);
|
|
12
|
+
var jwt__default = /*#__PURE__*/_interopDefault(jwt);
|
|
13
|
+
|
|
14
|
+
async function hashPassword(password, saltRounds = 12) {
|
|
15
|
+
return bcrypt__default.default.hash(password, saltRounds);
|
|
16
|
+
}
|
|
17
|
+
async function verifyPassword(password, hashedPassword) {
|
|
18
|
+
return bcrypt__default.default.compare(password, hashedPassword);
|
|
19
|
+
}
|
|
20
|
+
function generateToken(payload, secret, expiresIn = "7d") {
|
|
21
|
+
return jwt__default.default.sign(payload, secret, { expiresIn });
|
|
22
|
+
}
|
|
23
|
+
function verifyJwtToken(token, secret) {
|
|
24
|
+
return jwt__default.default.verify(token, secret);
|
|
25
|
+
}
|
|
26
|
+
function getTokenFromRequest(request) {
|
|
27
|
+
const cookieHeader = request.headers.get("Cookie");
|
|
28
|
+
if (cookieHeader) {
|
|
29
|
+
const match = cookieHeader.match(/auth_token=([^;]+)/);
|
|
30
|
+
if (match && match[1]) {
|
|
31
|
+
return match[1];
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
const authHeader = request.headers.get("Authorization");
|
|
35
|
+
if (authHeader && authHeader.startsWith("Bearer ")) {
|
|
36
|
+
const token = authHeader.substring(7);
|
|
37
|
+
return token || null;
|
|
38
|
+
}
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// src/auth/services/drizzle-auth-service.ts
|
|
43
|
+
var DrizzleAuthService = class {
|
|
44
|
+
constructor(config) {
|
|
45
|
+
this.config = {
|
|
46
|
+
db: config.db,
|
|
47
|
+
jwtSecret: config.jwtSecret,
|
|
48
|
+
jwtExpiresIn: config.jwtExpiresIn || "7d",
|
|
49
|
+
saltRounds: config.saltRounds || 12,
|
|
50
|
+
checkSecretStrength: config.checkSecretStrength !== false
|
|
51
|
+
};
|
|
52
|
+
this.validateConfig();
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* 验证配置
|
|
56
|
+
*/
|
|
57
|
+
validateConfig() {
|
|
58
|
+
if (!this.config.jwtSecret) {
|
|
59
|
+
throw new Error(
|
|
60
|
+
`JWT_SECRET is required. Please provide jwtSecret in config. You can generate a secure secret with: node -e "console.log(require('crypto').randomBytes(64).toString('hex'))"`
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
if (this.config.checkSecretStrength && process.env.NODE_ENV === "production" && this.config.jwtSecret.length < 32) {
|
|
64
|
+
throw new Error(
|
|
65
|
+
`JWT_SECRET is too short (${this.config.jwtSecret.length} chars, minimum 32 required in production)`
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* 用户注册
|
|
71
|
+
*/
|
|
72
|
+
async signUp(email, password, username, role = "USER") {
|
|
73
|
+
try {
|
|
74
|
+
const existingUser = await this.config.db.select().from(chunkSVWQN2LR_js.user).where(drizzleOrm.eq(chunkSVWQN2LR_js.user.email, email)).limit(1);
|
|
75
|
+
if (existingUser.length > 0) {
|
|
76
|
+
throw new Error("\u7528\u6237\u5DF2\u5B58\u5728");
|
|
77
|
+
}
|
|
78
|
+
const hashedPassword = await hashPassword(password, this.config.saltRounds);
|
|
79
|
+
const [newUser] = await this.config.db.insert(chunkSVWQN2LR_js.user).values({
|
|
80
|
+
id: crypto.randomBytes(16).toString("hex"),
|
|
81
|
+
email,
|
|
82
|
+
password: hashedPassword,
|
|
83
|
+
username: username || email.split("@")[0],
|
|
84
|
+
role,
|
|
85
|
+
emailVerified: false,
|
|
86
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
87
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
88
|
+
}).returning();
|
|
89
|
+
const token = generateToken(
|
|
90
|
+
{
|
|
91
|
+
userId: newUser.id,
|
|
92
|
+
email: newUser.email,
|
|
93
|
+
role: newUser.role
|
|
94
|
+
},
|
|
95
|
+
this.config.jwtSecret,
|
|
96
|
+
this.config.jwtExpiresIn
|
|
97
|
+
);
|
|
98
|
+
await this.createSession(newUser.id, token);
|
|
99
|
+
return {
|
|
100
|
+
user: {
|
|
101
|
+
id: newUser.id,
|
|
102
|
+
email: newUser.email,
|
|
103
|
+
username: newUser.username,
|
|
104
|
+
role: newUser.role
|
|
105
|
+
},
|
|
106
|
+
token
|
|
107
|
+
};
|
|
108
|
+
} catch (error) {
|
|
109
|
+
throw error;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* 用户登录
|
|
114
|
+
*/
|
|
115
|
+
async signIn(email, password) {
|
|
116
|
+
try {
|
|
117
|
+
const [foundUser] = await this.config.db.select().from(chunkSVWQN2LR_js.user).where(drizzleOrm.eq(chunkSVWQN2LR_js.user.email, email)).limit(1);
|
|
118
|
+
if (!foundUser) {
|
|
119
|
+
throw new Error("\u90AE\u7BB1\u6216\u5BC6\u7801\u9519\u8BEF");
|
|
120
|
+
}
|
|
121
|
+
if (!foundUser.password) {
|
|
122
|
+
throw new Error("\u7528\u6237\u5BC6\u7801\u672A\u8BBE\u7F6E");
|
|
123
|
+
}
|
|
124
|
+
const isPasswordValid = await verifyPassword(password, foundUser.password);
|
|
125
|
+
if (!isPasswordValid) {
|
|
126
|
+
throw new Error("\u90AE\u7BB1\u6216\u5BC6\u7801\u9519\u8BEF");
|
|
127
|
+
}
|
|
128
|
+
const token = generateToken(
|
|
129
|
+
{
|
|
130
|
+
userId: foundUser.id,
|
|
131
|
+
email: foundUser.email,
|
|
132
|
+
role: foundUser.role
|
|
133
|
+
},
|
|
134
|
+
this.config.jwtSecret,
|
|
135
|
+
this.config.jwtExpiresIn
|
|
136
|
+
);
|
|
137
|
+
await this.createSession(foundUser.id, token);
|
|
138
|
+
return {
|
|
139
|
+
user: {
|
|
140
|
+
id: foundUser.id,
|
|
141
|
+
email: foundUser.email,
|
|
142
|
+
username: foundUser.username,
|
|
143
|
+
role: foundUser.role
|
|
144
|
+
},
|
|
145
|
+
token
|
|
146
|
+
};
|
|
147
|
+
} catch (error) {
|
|
148
|
+
throw error;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* 验证 Token
|
|
153
|
+
*/
|
|
154
|
+
async verifyToken(token) {
|
|
155
|
+
try {
|
|
156
|
+
const decoded = verifyJwtToken(token, this.config.jwtSecret);
|
|
157
|
+
const [foundSession] = await this.config.db.select().from(chunkSVWQN2LR_js.session).where(
|
|
158
|
+
drizzleOrm.and(drizzleOrm.eq(chunkSVWQN2LR_js.session.token, token), drizzleOrm.gt(chunkSVWQN2LR_js.session.expiresAt, (/* @__PURE__ */ new Date()).toISOString()))
|
|
159
|
+
).limit(1);
|
|
160
|
+
if (!foundSession) {
|
|
161
|
+
throw new Error("\u4F1A\u8BDD\u65E0\u6548\u6216\u5DF2\u8FC7\u671F");
|
|
162
|
+
}
|
|
163
|
+
const [foundUser] = await this.config.db.select().from(chunkSVWQN2LR_js.user).where(drizzleOrm.eq(chunkSVWQN2LR_js.user.id, decoded.userId)).limit(1);
|
|
164
|
+
if (!foundUser) {
|
|
165
|
+
throw new Error("\u7528\u6237\u4E0D\u5B58\u5728");
|
|
166
|
+
}
|
|
167
|
+
return {
|
|
168
|
+
user: {
|
|
169
|
+
id: foundUser.id,
|
|
170
|
+
email: foundUser.email,
|
|
171
|
+
username: foundUser.username,
|
|
172
|
+
role: foundUser.role
|
|
173
|
+
},
|
|
174
|
+
session: foundSession
|
|
175
|
+
};
|
|
176
|
+
} catch (error) {
|
|
177
|
+
throw error;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* 创建会话
|
|
182
|
+
*/
|
|
183
|
+
async createSession(userId, token, ipAddress, userAgent) {
|
|
184
|
+
const expiresAt = /* @__PURE__ */ new Date();
|
|
185
|
+
expiresAt.setDate(expiresAt.getDate() + 7);
|
|
186
|
+
await this.config.db.insert(chunkSVWQN2LR_js.session).values({
|
|
187
|
+
id: crypto.randomBytes(16).toString("hex"),
|
|
188
|
+
userId,
|
|
189
|
+
token,
|
|
190
|
+
expiresAt: expiresAt.toISOString(),
|
|
191
|
+
ipAddress,
|
|
192
|
+
userAgent,
|
|
193
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
194
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* 删除会话(登出)
|
|
199
|
+
*/
|
|
200
|
+
async signOut(token) {
|
|
201
|
+
try {
|
|
202
|
+
await this.config.db.delete(chunkSVWQN2LR_js.session).where(drizzleOrm.eq(chunkSVWQN2LR_js.session.token, token));
|
|
203
|
+
return { success: true };
|
|
204
|
+
} catch (error) {
|
|
205
|
+
throw error;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* 获取会话
|
|
210
|
+
*/
|
|
211
|
+
async getSession(token) {
|
|
212
|
+
try {
|
|
213
|
+
return await this.verifyToken(token);
|
|
214
|
+
} catch (error) {
|
|
215
|
+
return null;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* 检查管理员权限
|
|
220
|
+
*/
|
|
221
|
+
async requireAdmin(token) {
|
|
222
|
+
const result = await this.verifyToken(token);
|
|
223
|
+
if (!["ADMIN", "SUPER_ADMIN"].includes(result.user.role)) {
|
|
224
|
+
throw new Error("\u9700\u8981\u7BA1\u7406\u5458\u6743\u9650");
|
|
225
|
+
}
|
|
226
|
+
return result;
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* 检查超级管理员权限
|
|
230
|
+
*/
|
|
231
|
+
async requireSuperAdmin(token) {
|
|
232
|
+
const result = await this.verifyToken(token);
|
|
233
|
+
if (result.user.role !== "SUPER_ADMIN") {
|
|
234
|
+
throw new Error("\u9700\u8981\u8D85\u7EA7\u7BA1\u7406\u5458\u6743\u9650");
|
|
235
|
+
}
|
|
236
|
+
return result;
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* 通过用户 ID 获取用户信息
|
|
240
|
+
*/
|
|
241
|
+
async getUserById(userId) {
|
|
242
|
+
try {
|
|
243
|
+
const [foundUser] = await this.config.db.select().from(chunkSVWQN2LR_js.user).where(drizzleOrm.eq(chunkSVWQN2LR_js.user.id, userId)).limit(1);
|
|
244
|
+
if (!foundUser) {
|
|
245
|
+
return null;
|
|
246
|
+
}
|
|
247
|
+
return {
|
|
248
|
+
id: foundUser.id,
|
|
249
|
+
email: foundUser.email,
|
|
250
|
+
username: foundUser.username,
|
|
251
|
+
role: foundUser.role
|
|
252
|
+
};
|
|
253
|
+
} catch (error) {
|
|
254
|
+
return null;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* 通过邮箱获取用户信息
|
|
259
|
+
*/
|
|
260
|
+
async getUserByEmail(email) {
|
|
261
|
+
try {
|
|
262
|
+
const [foundUser] = await this.config.db.select().from(chunkSVWQN2LR_js.user).where(drizzleOrm.eq(chunkSVWQN2LR_js.user.email, email)).limit(1);
|
|
263
|
+
if (!foundUser) {
|
|
264
|
+
return null;
|
|
265
|
+
}
|
|
266
|
+
return {
|
|
267
|
+
id: foundUser.id,
|
|
268
|
+
email: foundUser.email,
|
|
269
|
+
username: foundUser.username,
|
|
270
|
+
role: foundUser.role
|
|
271
|
+
};
|
|
272
|
+
} catch (error) {
|
|
273
|
+
return null;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
exports.DrizzleAuthService = DrizzleAuthService;
|
|
279
|
+
exports.generateToken = generateToken;
|
|
280
|
+
exports.getTokenFromRequest = getTokenFromRequest;
|
|
281
|
+
exports.hashPassword = hashPassword;
|
|
282
|
+
exports.verifyJwtToken = verifyJwtToken;
|
|
283
|
+
exports.verifyPassword = verifyPassword;
|
|
284
|
+
//# sourceMappingURL=chunk-6BL3AZGD.js.map
|
|
285
|
+
//# sourceMappingURL=chunk-6BL3AZGD.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/auth/services/password-utils.ts","../src/auth/services/token-utils.ts","../src/auth/services/drizzle-auth-service.ts"],"names":["bcrypt","jwt","user","eq","randomBytes","session","and","gt"],"mappings":"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","file":"chunk-6BL3AZGD.js","sourcesContent":["/**\n * Auth Services - Password Utilities\n * 密码哈希相关工具函数\n */\n\nimport bcrypt from 'bcryptjs';\n\n/**\n * 哈希密码\n */\nexport async function hashPassword(password: string, saltRounds: number = 12): Promise<string> {\n return bcrypt.hash(password, saltRounds);\n}\n\n/**\n * 验证密码\n */\nexport async function verifyPassword(password: string, hashedPassword: string): Promise<boolean> {\n return bcrypt.compare(password, hashedPassword);\n}\n\n","/**\n * Auth Services - Token Utilities\n * Token 相关工具函数\n */\n\nimport jwt from 'jsonwebtoken';\nimport type { UserRole } from '../schema/enums';\n\n/**\n * JWT Payload\n */\nexport interface JwtPayload {\n userId: string;\n email: string;\n role: UserRole;\n iat?: number;\n exp?: number;\n}\n\n/**\n * 生成 JWT Token\n */\nexport function generateToken(\n payload: Omit<JwtPayload, 'iat' | 'exp'>,\n secret: string,\n expiresIn: string | number = '7d'\n): string {\n return jwt.sign(payload, secret, { expiresIn } as jwt.SignOptions);\n}\n\n/**\n * 验证 JWT Token\n */\nexport function verifyJwtToken(token: string, secret: string): JwtPayload {\n return jwt.verify(token, secret) as JwtPayload;\n}\n\n/**\n * 从请求中获取 Token\n * 优先从 Cookie 读取(Web),兼容 Authorization Header(Mobile/API)\n */\nexport function getTokenFromRequest(request: Request): string | null {\n // 🔐 优先从 httpOnly Cookie 读取(Web 管理后台,更安全)\n const cookieHeader = request.headers.get('Cookie');\n if (cookieHeader) {\n // 匹配 auth_token\n const match = cookieHeader.match(/auth_token=([^;]+)/);\n if (match && match[1]) {\n return match[1];\n }\n }\n\n // 🔄 兼容从 Authorization Header 读取(移动端、小程序、API 调用)\n const authHeader = request.headers.get('Authorization');\n if (authHeader && authHeader.startsWith('Bearer ')) {\n const token = authHeader.substring(7);\n return token || null;\n }\n\n return null;\n}\n\n","/**\n * Auth Services - Drizzle Auth Service\n * 基于 Drizzle ORM 的认证服务\n */\n\nimport { eq, and, gt } from 'drizzle-orm';\nimport { randomBytes } from 'crypto';\nimport { user, session } from '../schema';\nimport { hashPassword, verifyPassword } from './password-utils';\nimport { generateToken, verifyJwtToken } from './token-utils';\nimport type {\n AuthServiceConfig,\n AuthResult,\n VerifyResult,\n UserInfo,\n SessionInfo,\n} from './types';\nimport type { UserRole } from '../schema/enums';\n\n/**\n * Drizzle 认证服务类\n *\n * @example\n * ```typescript\n * import { DrizzleAuthService } from '@qhr123/sa2kit/auth/services';\n * import { db } from './db';\n *\n * const authService = new DrizzleAuthService({\n * db,\n * jwtSecret: process.env.JWT_SECRET!,\n * jwtExpiresIn: '7d',\n * });\n *\n * // 用户注册\n * const result = await authService.signUp('user@example.com', 'password123', 'username');\n *\n * // 用户登录\n * const loginResult = await authService.signIn('user@example.com', 'password123');\n * ```\n */\nexport class DrizzleAuthService {\n private config: Required<AuthServiceConfig>;\n\n constructor(config: AuthServiceConfig) {\n // 设置默认值\n this.config = {\n db: config.db,\n jwtSecret: config.jwtSecret,\n jwtExpiresIn: config.jwtExpiresIn || '7d',\n saltRounds: config.saltRounds || 12,\n checkSecretStrength: config.checkSecretStrength !== false,\n };\n\n // 验证配置\n this.validateConfig();\n }\n\n /**\n * 验证配置\n */\n private validateConfig(): void {\n if (!this.config.jwtSecret) {\n throw new Error(\n 'JWT_SECRET is required. Please provide jwtSecret in config. ' +\n \"You can generate a secure secret with: node -e \\\"console.log(require('crypto').randomBytes(64).toString('hex'))\\\"\"\n );\n }\n\n // 生产环境检查密钥强度\n if (\n this.config.checkSecretStrength &&\n process.env.NODE_ENV === 'production' &&\n this.config.jwtSecret.length < 32\n ) {\n throw new Error(\n `JWT_SECRET is too short (${this.config.jwtSecret.length} chars, minimum 32 required in production)`\n );\n }\n }\n\n /**\n * 用户注册\n */\n async signUp(\n email: string,\n password: string,\n username?: string,\n role: UserRole = 'USER'\n ): Promise<AuthResult> {\n try {\n // 检查用户是否已存在\n const existingUser = await this.config.db\n .select()\n .from(user)\n .where(eq(user.email, email))\n .limit(1);\n\n if (existingUser.length > 0) {\n throw new Error('用户已存在');\n }\n\n // 哈希密码\n const hashedPassword = await hashPassword(password, this.config.saltRounds);\n\n // 创建用户\n const [newUser] = await this.config.db\n .insert(user)\n .values({\n id: randomBytes(16).toString('hex'),\n email,\n password: hashedPassword,\n username: username || email.split('@')[0],\n role,\n emailVerified: false,\n createdAt: new Date().toISOString(),\n updatedAt: new Date().toISOString(),\n })\n .returning();\n\n // 生成 JWT token\n const token = generateToken(\n {\n userId: newUser.id,\n email: newUser.email,\n role: newUser.role as UserRole,\n },\n this.config.jwtSecret,\n this.config.jwtExpiresIn\n );\n\n // 创建会话\n await this.createSession(newUser.id, token);\n\n return {\n user: {\n id: newUser.id,\n email: newUser.email,\n username: newUser.username,\n role: newUser.role as UserRole,\n },\n token,\n };\n } catch (error) {\n throw error;\n }\n }\n\n /**\n * 用户登录\n */\n async signIn(email: string, password: string): Promise<AuthResult> {\n try {\n // 查找用户\n const [foundUser] = await this.config.db\n .select()\n .from(user)\n .where(eq(user.email, email))\n .limit(1);\n\n if (!foundUser) {\n throw new Error('邮箱或密码错误');\n }\n\n // 验证密码\n if (!foundUser.password) {\n throw new Error('用户密码未设置');\n }\n\n const isPasswordValid = await verifyPassword(password, foundUser.password);\n if (!isPasswordValid) {\n throw new Error('邮箱或密码错误');\n }\n\n // 生成 JWT token\n const token = generateToken(\n {\n userId: foundUser.id,\n email: foundUser.email,\n role: foundUser.role as UserRole,\n },\n this.config.jwtSecret,\n this.config.jwtExpiresIn\n );\n\n // 创建会话\n await this.createSession(foundUser.id, token);\n\n return {\n user: {\n id: foundUser.id,\n email: foundUser.email,\n username: foundUser.username,\n role: foundUser.role as UserRole,\n },\n token,\n };\n } catch (error) {\n throw error;\n }\n }\n\n /**\n * 验证 Token\n */\n async verifyToken(token: string): Promise<VerifyResult> {\n try {\n // 验证 JWT\n const decoded = verifyJwtToken(token, this.config.jwtSecret);\n\n // 检查会话是否存在且未过期\n const [foundSession] = await this.config.db\n .select()\n .from(session)\n .where(\n and(eq(session.token, token), gt(session.expiresAt, new Date().toISOString()))\n )\n .limit(1);\n\n if (!foundSession) {\n throw new Error('会话无效或已过期');\n }\n\n // 获取用户信息\n const [foundUser] = await this.config.db\n .select()\n .from(user)\n .where(eq(user.id, decoded.userId))\n .limit(1);\n\n if (!foundUser) {\n throw new Error('用户不存在');\n }\n\n return {\n user: {\n id: foundUser.id,\n email: foundUser.email,\n username: foundUser.username,\n role: foundUser.role as UserRole,\n },\n session: foundSession as SessionInfo,\n };\n } catch (error) {\n throw error;\n }\n }\n\n /**\n * 创建会话\n */\n async createSession(\n userId: string,\n token: string,\n ipAddress?: string,\n userAgent?: string\n ): Promise<void> {\n const expiresAt = new Date();\n expiresAt.setDate(expiresAt.getDate() + 7); // 7天后过期\n\n await this.config.db.insert(session).values({\n id: randomBytes(16).toString('hex'),\n userId,\n token,\n expiresAt: expiresAt.toISOString(),\n ipAddress,\n userAgent,\n createdAt: new Date().toISOString(),\n updatedAt: new Date().toISOString(),\n });\n }\n\n /**\n * 删除会话(登出)\n */\n async signOut(token: string): Promise<{ success: boolean }> {\n try {\n await this.config.db.delete(session).where(eq(session.token, token));\n return { success: true };\n } catch (error) {\n throw error;\n }\n }\n\n /**\n * 获取会话\n */\n async getSession(token: string): Promise<VerifyResult | null> {\n try {\n return await this.verifyToken(token);\n } catch (error) {\n return null;\n }\n }\n\n /**\n * 检查管理员权限\n */\n async requireAdmin(token: string): Promise<VerifyResult> {\n const result = await this.verifyToken(token);\n if (!['ADMIN', 'SUPER_ADMIN'].includes(result.user.role)) {\n throw new Error('需要管理员权限');\n }\n return result;\n }\n\n /**\n * 检查超级管理员权限\n */\n async requireSuperAdmin(token: string): Promise<VerifyResult> {\n const result = await this.verifyToken(token);\n if (result.user.role !== 'SUPER_ADMIN') {\n throw new Error('需要超级管理员权限');\n }\n return result;\n }\n\n /**\n * 通过用户 ID 获取用户信息\n */\n async getUserById(userId: string): Promise<UserInfo | null> {\n try {\n const [foundUser] = await this.config.db\n .select()\n .from(user)\n .where(eq(user.id, userId))\n .limit(1);\n\n if (!foundUser) {\n return null;\n }\n\n return {\n id: foundUser.id,\n email: foundUser.email,\n username: foundUser.username,\n role: foundUser.role as UserRole,\n };\n } catch (error) {\n return null;\n }\n }\n\n /**\n * 通过邮箱获取用户信息\n */\n async getUserByEmail(email: string): Promise<UserInfo | null> {\n try {\n const [foundUser] = await this.config.db\n .select()\n .from(user)\n .where(eq(user.email, email))\n .limit(1);\n\n if (!foundUser) {\n return null;\n }\n\n return {\n id: foundUser.id,\n email: foundUser.email,\n username: foundUser.username,\n role: foundUser.role as UserRole,\n };\n } catch (error) {\n return null;\n }\n }\n}\n\n"]}
|