@symbo.ls/sdk 2.34.35 → 3.1.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.
- package/README.md +2 -143
- package/dist/cjs/config/environment.js +30 -98
- package/dist/cjs/index.js +24 -144
- package/dist/cjs/services/AIService.js +155 -0
- package/dist/cjs/services/AuthService.js +305 -738
- package/dist/cjs/services/BaseService.js +6 -158
- package/dist/cjs/services/BasedService.js +1185 -0
- package/dist/cjs/services/CoreService.js +1751 -0
- package/dist/cjs/services/SocketIOService.js +307 -0
- package/dist/cjs/services/SocketService.js +161 -0
- package/dist/cjs/services/SymstoryService.js +571 -0
- package/dist/cjs/services/index.js +16 -64
- package/dist/cjs/utils/TokenManager.js +30 -78
- package/dist/cjs/utils/basedQuerys.js +181 -0
- package/dist/cjs/utils/services.js +103 -301
- package/dist/cjs/utils/symstoryClient.js +259 -0
- package/dist/cjs/utils/validation.js +3 -0
- package/dist/esm/config/environment.js +30 -98
- package/dist/esm/index.js +8797 -49416
- package/dist/esm/services/AIService.js +185 -0
- package/dist/esm/services/AuthService.js +386 -1493
- package/dist/esm/services/BaseService.js +6 -757
- package/dist/esm/services/BasedService.js +5278 -0
- package/dist/esm/services/CoreService.js +2264 -0
- package/dist/esm/services/SocketIOService.js +470 -0
- package/dist/esm/services/SocketService.js +191 -0
- package/dist/esm/services/SymstoryService.js +7041 -0
- package/dist/esm/services/index.js +8690 -49015
- package/dist/esm/utils/TokenManager.js +30 -78
- package/dist/esm/utils/basedQuerys.js +163 -0
- package/dist/esm/utils/services.js +103 -301
- package/dist/esm/utils/symstoryClient.js +370 -0
- package/dist/esm/utils/validation.js +7 -4
- package/dist/node/config/environment.js +30 -98
- package/dist/node/index.js +32 -175
- package/dist/node/services/AIService.js +136 -0
- package/dist/node/services/AuthService.js +310 -742
- package/dist/node/services/BaseService.js +6 -148
- package/dist/node/services/BasedService.js +1156 -0
- package/dist/node/services/CoreService.js +1722 -0
- package/dist/node/services/SocketIOService.js +278 -0
- package/dist/node/services/SocketService.js +142 -0
- package/dist/node/services/SymstoryService.js +542 -0
- package/dist/node/services/index.js +16 -64
- package/dist/node/utils/TokenManager.js +30 -78
- package/dist/node/utils/basedQuerys.js +162 -0
- package/dist/node/utils/services.js +103 -301
- package/dist/node/utils/symstoryClient.js +230 -0
- package/dist/node/utils/validation.js +3 -0
- package/package.json +16 -35
- package/src/config/environment.js +28 -99
- package/src/index.js +36 -181
- package/src/services/AIService.js +150 -0
- package/src/services/AuthService.js +328 -874
- package/src/services/BaseService.js +6 -166
- package/src/services/BasedService.js +1301 -0
- package/src/services/CoreService.js +1943 -0
- package/src/services/SocketIOService.js +334 -0
- package/src/services/SocketService.js +168 -0
- package/src/services/SymstoryService.js +649 -0
- package/src/services/index.js +13 -80
- package/src/utils/TokenManager.js +33 -88
- package/src/utils/basedQuerys.js +164 -0
- package/src/utils/services.js +107 -326
- package/src/utils/symstoryClient.js +252 -0
- package/src/utils/validation.js +3 -0
- package/dist/cjs/services/AdminService.js +0 -351
- package/dist/cjs/services/BranchService.js +0 -484
- package/dist/cjs/services/CollabService.js +0 -743
- package/dist/cjs/services/DnsService.js +0 -340
- package/dist/cjs/services/FeatureFlagService.js +0 -175
- package/dist/cjs/services/FileService.js +0 -201
- package/dist/cjs/services/IntegrationService.js +0 -538
- package/dist/cjs/services/MetricsService.js +0 -62
- package/dist/cjs/services/PaymentService.js +0 -271
- package/dist/cjs/services/PlanService.js +0 -426
- package/dist/cjs/services/ProjectService.js +0 -1207
- package/dist/cjs/services/PullRequestService.js +0 -503
- package/dist/cjs/services/ScreenshotService.js +0 -304
- package/dist/cjs/services/SubscriptionService.js +0 -396
- package/dist/cjs/services/TrackingService.js +0 -661
- package/dist/cjs/services/WaitlistService.js +0 -148
- package/dist/cjs/state/RootStateManager.js +0 -65
- package/dist/cjs/state/rootEventBus.js +0 -74
- package/dist/cjs/utils/CollabClient.js +0 -223
- package/dist/cjs/utils/changePreprocessor.js +0 -199
- package/dist/cjs/utils/jsonDiff.js +0 -145
- package/dist/cjs/utils/ordering.js +0 -309
- package/dist/esm/services/AdminService.js +0 -1132
- package/dist/esm/services/BranchService.js +0 -1265
- package/dist/esm/services/CollabService.js +0 -26838
- package/dist/esm/services/DnsService.js +0 -1121
- package/dist/esm/services/FeatureFlagService.js +0 -956
- package/dist/esm/services/FileService.js +0 -982
- package/dist/esm/services/IntegrationService.js +0 -1319
- package/dist/esm/services/MetricsService.js +0 -843
- package/dist/esm/services/PaymentService.js +0 -1052
- package/dist/esm/services/PlanService.js +0 -1207
- package/dist/esm/services/ProjectService.js +0 -2526
- package/dist/esm/services/PullRequestService.js +0 -1284
- package/dist/esm/services/ScreenshotService.js +0 -1085
- package/dist/esm/services/SubscriptionService.js +0 -1177
- package/dist/esm/services/TrackingService.js +0 -18343
- package/dist/esm/services/WaitlistService.js +0 -929
- package/dist/esm/state/RootStateManager.js +0 -90
- package/dist/esm/state/rootEventBus.js +0 -56
- package/dist/esm/utils/CollabClient.js +0 -18901
- package/dist/esm/utils/changePreprocessor.js +0 -542
- package/dist/esm/utils/jsonDiff.js +0 -7011
- package/dist/esm/utils/ordering.js +0 -291
- package/dist/node/services/AdminService.js +0 -332
- package/dist/node/services/BranchService.js +0 -465
- package/dist/node/services/CollabService.js +0 -724
- package/dist/node/services/DnsService.js +0 -321
- package/dist/node/services/FeatureFlagService.js +0 -156
- package/dist/node/services/FileService.js +0 -182
- package/dist/node/services/IntegrationService.js +0 -519
- package/dist/node/services/MetricsService.js +0 -43
- package/dist/node/services/PaymentService.js +0 -252
- package/dist/node/services/PlanService.js +0 -407
- package/dist/node/services/ProjectService.js +0 -1188
- package/dist/node/services/PullRequestService.js +0 -484
- package/dist/node/services/ScreenshotService.js +0 -285
- package/dist/node/services/SubscriptionService.js +0 -377
- package/dist/node/services/TrackingService.js +0 -632
- package/dist/node/services/WaitlistService.js +0 -129
- package/dist/node/state/RootStateManager.js +0 -36
- package/dist/node/state/rootEventBus.js +0 -55
- package/dist/node/utils/CollabClient.js +0 -194
- package/dist/node/utils/changePreprocessor.js +0 -180
- package/dist/node/utils/jsonDiff.js +0 -116
- package/dist/node/utils/ordering.js +0 -290
- package/src/services/AdminService.js +0 -374
- package/src/services/BranchService.js +0 -536
- package/src/services/CollabService.js +0 -900
- package/src/services/DnsService.js +0 -366
- package/src/services/FeatureFlagService.js +0 -174
- package/src/services/FileService.js +0 -213
- package/src/services/IntegrationService.js +0 -548
- package/src/services/MetricsService.js +0 -40
- package/src/services/PaymentService.js +0 -287
- package/src/services/PlanService.js +0 -468
- package/src/services/ProjectService.js +0 -1366
- package/src/services/PullRequestService.js +0 -537
- package/src/services/ScreenshotService.js +0 -258
- package/src/services/SubscriptionService.js +0 -425
- package/src/services/TrackingService.js +0 -853
- package/src/services/WaitlistService.js +0 -130
- package/src/services/tests/BranchService/createBranch.test.js +0 -153
- package/src/services/tests/BranchService/deleteBranch.test.js +0 -173
- package/src/services/tests/BranchService/getBranchChanges.test.js +0 -146
- package/src/services/tests/BranchService/listBranches.test.js +0 -87
- package/src/services/tests/BranchService/mergeBranch.test.js +0 -210
- package/src/services/tests/BranchService/publishVersion.test.js +0 -183
- package/src/services/tests/BranchService/renameBranch.test.js +0 -240
- package/src/services/tests/BranchService/resetBranch.test.js +0 -152
- package/src/services/tests/FeatureFlagService/adminFeatureFlags.test.js +0 -67
- package/src/services/tests/FeatureFlagService/getFeatureFlags.test.js +0 -75
- package/src/services/tests/FileService/createFileFormData.test.js +0 -74
- package/src/services/tests/FileService/getFileUrl.test.js +0 -69
- package/src/services/tests/FileService/updateProjectIcon.test.js +0 -109
- package/src/services/tests/FileService/uploadDocument.test.js +0 -36
- package/src/services/tests/FileService/uploadFile.test.js +0 -78
- package/src/services/tests/FileService/uploadFileWithValidation.test.js +0 -114
- package/src/services/tests/FileService/uploadImage.test.js +0 -36
- package/src/services/tests/FileService/uploadMultipleFiles.test.js +0 -111
- package/src/services/tests/FileService/validateFile.test.js +0 -63
- package/src/services/tests/PlanService/createPlan.test.js +0 -104
- package/src/services/tests/PlanService/createPlanWithValidation.test.js +0 -523
- package/src/services/tests/PlanService/deletePlan.test.js +0 -92
- package/src/services/tests/PlanService/getActivePlans.test.js +0 -123
- package/src/services/tests/PlanService/getAdminPlans.test.js +0 -84
- package/src/services/tests/PlanService/getPlan.test.js +0 -50
- package/src/services/tests/PlanService/getPlanByKey.test.js +0 -109
- package/src/services/tests/PlanService/getPlanWithValidation.test.js +0 -85
- package/src/services/tests/PlanService/getPlans.test.js +0 -53
- package/src/services/tests/PlanService/getPlansByPriceRange.test.js +0 -109
- package/src/services/tests/PlanService/getPlansWithValidation.test.js +0 -48
- package/src/services/tests/PlanService/initializePlans.test.js +0 -75
- package/src/services/tests/PlanService/updatePlan.test.js +0 -111
- package/src/services/tests/PlanService/updatePlanWithValidation.test.js +0 -556
- package/src/state/RootStateManager.js +0 -76
- package/src/state/rootEventBus.js +0 -67
- package/src/utils/CollabClient.js +0 -248
- package/src/utils/changePreprocessor.js +0 -239
- package/src/utils/jsonDiff.js +0 -144
- package/src/utils/ordering.js +0 -271
|
@@ -1,885 +1,220 @@
|
|
|
1
|
+
/* eslint-disable require-await */
|
|
1
2
|
import { BaseService } from './BaseService.js'
|
|
2
3
|
import {
|
|
4
|
+
PERMISSION_MAP,
|
|
3
5
|
ROLE_PERMISSIONS,
|
|
4
6
|
TIER_FEATURES,
|
|
5
7
|
PROJECT_ROLE_PERMISSIONS
|
|
6
8
|
} from '../utils/permission.js'
|
|
7
9
|
|
|
8
|
-
const PLUGIN_SESSION_STORAGE_KEY = 'plugin_auth_session'
|
|
9
|
-
|
|
10
10
|
export class AuthService extends BaseService {
|
|
11
|
-
constructor(config) {
|
|
11
|
+
constructor (config) {
|
|
12
12
|
super(config)
|
|
13
13
|
this._userRoles = new Set(['guest', 'editor', 'admin', 'owner'])
|
|
14
14
|
this._projectTiers = new Set([
|
|
15
15
|
'ready',
|
|
16
|
-
'
|
|
16
|
+
'free',
|
|
17
17
|
'pro1',
|
|
18
18
|
'pro2',
|
|
19
19
|
'enterprise'
|
|
20
20
|
])
|
|
21
|
-
this.
|
|
22
|
-
this._roleCacheExpiry = 5 * 60 * 1000 // 5 minutes cache expiry
|
|
23
|
-
this._pluginSession = null
|
|
24
|
-
|
|
25
|
-
this._resolvePluginSession(
|
|
26
|
-
config?.session ||
|
|
27
|
-
config?.pluginSession ||
|
|
28
|
-
config?.options?.pluginSession ||
|
|
29
|
-
config?.context?.pluginSession ||
|
|
30
|
-
null
|
|
31
|
-
)
|
|
21
|
+
this._initialized = false
|
|
32
22
|
}
|
|
33
23
|
|
|
34
|
-
//
|
|
35
|
-
|
|
36
|
-
// ==================== AUTH METHODS ====================
|
|
37
|
-
|
|
38
|
-
async register(userData, options = {}) {
|
|
24
|
+
// eslint-disable-next-line no-empty-pattern
|
|
25
|
+
init ({}) {
|
|
39
26
|
try {
|
|
40
|
-
const {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
})
|
|
50
|
-
if (response.success) {
|
|
51
|
-
if (session) {
|
|
52
|
-
this._clearPluginSession(session)
|
|
27
|
+
const { authToken, appKey } = this._context || {}
|
|
28
|
+
|
|
29
|
+
// Store masked configuration info
|
|
30
|
+
this._info = {
|
|
31
|
+
config: {
|
|
32
|
+
appKey: appKey
|
|
33
|
+
? `${appKey.substr(0, 4)}...${appKey.substr(-4)}`
|
|
34
|
+
: undefined, // eslint-disable-line no-undefined
|
|
35
|
+
hasToken: Boolean(authToken)
|
|
53
36
|
}
|
|
54
|
-
return response.data
|
|
55
37
|
}
|
|
56
|
-
throw new Error(response.message)
|
|
57
|
-
} catch (error) {
|
|
58
|
-
throw new Error(`Registration failed: ${error.message}`, { cause: error })
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
38
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
const { payload, session } = this._preparePluginPayload(
|
|
65
|
-
{
|
|
66
|
-
email,
|
|
67
|
-
password
|
|
68
|
-
},
|
|
69
|
-
options.session
|
|
70
|
-
)
|
|
71
|
-
|
|
72
|
-
const response = await this._request('/auth/login', {
|
|
73
|
-
method: 'POST',
|
|
74
|
-
body: JSON.stringify(payload),
|
|
75
|
-
methodName: 'login'
|
|
76
|
-
})
|
|
77
|
-
|
|
78
|
-
// Handle new response format: response.data.tokens
|
|
79
|
-
if (response.success && response.data && response.data.tokens) {
|
|
80
|
-
const { tokens } = response.data
|
|
81
|
-
const tokenData = {
|
|
82
|
-
access_token: tokens.accessToken,
|
|
83
|
-
refresh_token: tokens.refreshToken,
|
|
84
|
-
expires_in: tokens.accessTokenExp?.expiresIn,
|
|
85
|
-
token_type: 'Bearer'
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
// Set tokens in TokenManager (will handle persistence and refresh scheduling)
|
|
89
|
-
if (this._tokenManager) {
|
|
90
|
-
this._tokenManager.setTokens(tokenData)
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
if (response.success) {
|
|
95
|
-
if (session) {
|
|
96
|
-
this._clearPluginSession(session)
|
|
97
|
-
}
|
|
98
|
-
return response.data
|
|
99
|
-
}
|
|
100
|
-
throw new Error(response.message)
|
|
39
|
+
this._initialized = true
|
|
40
|
+
this._setReady()
|
|
101
41
|
} catch (error) {
|
|
102
|
-
|
|
42
|
+
this._setError(error)
|
|
43
|
+
throw error
|
|
103
44
|
}
|
|
104
45
|
}
|
|
105
46
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
}
|
|
119
|
-
} catch (error) {
|
|
120
|
-
// Even if the API call fails, clear local tokens
|
|
121
|
-
if (this._tokenManager) {
|
|
122
|
-
this._tokenManager.clearTokens()
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
throw new Error(`Logout failed: ${error.message}`, { cause: error })
|
|
126
|
-
}
|
|
47
|
+
_requiresInit (methodName) {
|
|
48
|
+
const noInitMethods = new Set([
|
|
49
|
+
'users:login',
|
|
50
|
+
'users:register',
|
|
51
|
+
'users:request-password-reset',
|
|
52
|
+
'users:reset-password',
|
|
53
|
+
'users:reset-password-confirm',
|
|
54
|
+
'users:register-confirmation',
|
|
55
|
+
'users:google-auth',
|
|
56
|
+
'users:github-auth'
|
|
57
|
+
])
|
|
58
|
+
return !noInitMethods.has(methodName)
|
|
127
59
|
}
|
|
128
60
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
method: 'POST',
|
|
133
|
-
body: JSON.stringify({ refreshToken }),
|
|
134
|
-
methodName: 'refreshToken'
|
|
135
|
-
})
|
|
136
|
-
if (response.success) {
|
|
137
|
-
return response.data
|
|
138
|
-
}
|
|
139
|
-
throw new Error(response.message)
|
|
140
|
-
} catch (error) {
|
|
141
|
-
throw new Error(`Token refresh failed: ${error.message}`, { cause: error })
|
|
61
|
+
_requireReady (methodName) {
|
|
62
|
+
if (this._requiresInit(methodName) && !this._initialized) {
|
|
63
|
+
throw new Error('Service not initialized')
|
|
142
64
|
}
|
|
143
65
|
}
|
|
144
66
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
payload.inviteToken = inviteToken
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
const response = await this._request('/auth/google', {
|
|
153
|
-
method: 'POST',
|
|
154
|
-
body: JSON.stringify(payload),
|
|
155
|
-
methodName: 'googleAuth'
|
|
156
|
-
})
|
|
157
|
-
|
|
158
|
-
// Handle new response format: response.data.tokens
|
|
159
|
-
if (response.success && response.data && response.data.tokens) {
|
|
160
|
-
const { tokens } = response.data
|
|
161
|
-
const tokenData = {
|
|
162
|
-
access_token: tokens.accessToken,
|
|
163
|
-
refresh_token: tokens.refreshToken,
|
|
164
|
-
expires_in: tokens.accessTokenExp?.expiresIn,
|
|
165
|
-
token_type: 'Bearer'
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
// Set tokens in TokenManager
|
|
169
|
-
if (this._tokenManager) {
|
|
170
|
-
this._tokenManager.setTokens(tokenData)
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
if (response.success) {
|
|
175
|
-
if (session) {
|
|
176
|
-
this._clearPluginSession(session)
|
|
177
|
-
}
|
|
178
|
-
return response.data
|
|
179
|
-
}
|
|
180
|
-
throw new Error(response.message)
|
|
181
|
-
} catch (error) {
|
|
182
|
-
throw new Error(`Google auth failed: ${error.message}`, { cause: error })
|
|
67
|
+
_getBasedService (methodName) {
|
|
68
|
+
const based = this._context.services?.based
|
|
69
|
+
if (this._requiresInit(methodName) && !based) {
|
|
70
|
+
throw new Error('Based service not available')
|
|
183
71
|
}
|
|
72
|
+
return based._client
|
|
184
73
|
}
|
|
185
74
|
|
|
186
|
-
async
|
|
75
|
+
async login (identifier, password) {
|
|
187
76
|
try {
|
|
188
|
-
const
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
77
|
+
const based = this._getBasedService('login')
|
|
78
|
+
const response = await based.call('users:login', { identifier, password })
|
|
79
|
+
|
|
80
|
+
if (this._initialized) {
|
|
81
|
+
this.updateContext({ authToken: response.token })
|
|
82
|
+
}
|
|
83
|
+
based.setAuthState({
|
|
84
|
+
token: response.token,
|
|
85
|
+
userId: response.userId,
|
|
86
|
+
projectRoles: response.projectRoles,
|
|
87
|
+
globalRole: response.globalRole,
|
|
88
|
+
persistent: true
|
|
197
89
|
})
|
|
198
90
|
|
|
199
|
-
|
|
200
|
-
if (response.success && response.data && response.data.tokens) {
|
|
201
|
-
const { tokens } = response.data
|
|
202
|
-
const tokenData = {
|
|
203
|
-
access_token: tokens.accessToken,
|
|
204
|
-
refresh_token: tokens.refreshToken,
|
|
205
|
-
expires_in: tokens.accessTokenExp?.expiresIn,
|
|
206
|
-
token_type: 'Bearer'
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
// Set tokens in TokenManager
|
|
210
|
-
if (this._tokenManager) {
|
|
211
|
-
this._tokenManager.setTokens(tokenData)
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
if (response.success) {
|
|
216
|
-
if (session) {
|
|
217
|
-
this._clearPluginSession(session)
|
|
218
|
-
}
|
|
219
|
-
return response.data
|
|
220
|
-
}
|
|
221
|
-
throw new Error(response.message)
|
|
91
|
+
return response
|
|
222
92
|
} catch (error) {
|
|
223
|
-
throw new Error(`
|
|
93
|
+
throw new Error(`Login failed: ${error.message}`)
|
|
224
94
|
}
|
|
225
95
|
}
|
|
226
96
|
|
|
227
|
-
async
|
|
97
|
+
async register (userData) {
|
|
228
98
|
try {
|
|
229
|
-
const
|
|
230
|
-
|
|
231
|
-
options.session
|
|
232
|
-
)
|
|
233
|
-
if (inviteToken) {
|
|
234
|
-
body.inviteToken = inviteToken
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
const response = await this._request('/auth/google/callback', {
|
|
238
|
-
method: 'POST',
|
|
239
|
-
body: JSON.stringify(body),
|
|
240
|
-
methodName: 'googleAuthCallback'
|
|
241
|
-
})
|
|
242
|
-
|
|
243
|
-
// Handle new response format: response.data.tokens
|
|
244
|
-
if (response.success && response.data && response.data.tokens) {
|
|
245
|
-
const { tokens } = response.data
|
|
246
|
-
const tokenData = {
|
|
247
|
-
access_token: tokens.accessToken,
|
|
248
|
-
refresh_token: tokens.refreshToken,
|
|
249
|
-
expires_in: tokens.accessTokenExp?.expiresIn,
|
|
250
|
-
token_type: 'Bearer'
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
// Set tokens in TokenManager
|
|
254
|
-
if (this._tokenManager) {
|
|
255
|
-
this._tokenManager.setTokens(tokenData)
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
if (response.success) {
|
|
260
|
-
if (session) {
|
|
261
|
-
this._clearPluginSession(session)
|
|
262
|
-
}
|
|
263
|
-
return response.data
|
|
264
|
-
}
|
|
265
|
-
throw new Error(response.message)
|
|
99
|
+
const based = this._getBasedService('register')
|
|
100
|
+
return await based.call('users:register', userData)
|
|
266
101
|
} catch (error) {
|
|
267
|
-
throw new Error(`
|
|
102
|
+
throw new Error(`Registration failed: ${error.message}`)
|
|
268
103
|
}
|
|
269
104
|
}
|
|
270
105
|
|
|
271
|
-
async
|
|
106
|
+
async googleAuth (idToken) {
|
|
272
107
|
try {
|
|
273
|
-
const
|
|
274
|
-
|
|
275
|
-
body: JSON.stringify({ email }),
|
|
276
|
-
methodName: 'requestPasswordReset'
|
|
277
|
-
})
|
|
278
|
-
if (response.success) {
|
|
279
|
-
return response.data
|
|
280
|
-
}
|
|
281
|
-
throw new Error(response.message)
|
|
282
|
-
} catch (error) {
|
|
283
|
-
throw new Error(`Password reset request failed: ${error.message}`, { cause: error })
|
|
284
|
-
}
|
|
285
|
-
}
|
|
108
|
+
const based = this._getBasedService('googleAuth')
|
|
109
|
+
const response = await based.call('users:google-auth', { idToken })
|
|
286
110
|
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
const response = await this._request('/auth/reset-password-confirm', {
|
|
290
|
-
method: 'POST',
|
|
291
|
-
body: JSON.stringify({ token, password }),
|
|
292
|
-
methodName: 'confirmPasswordReset'
|
|
293
|
-
})
|
|
294
|
-
if (response.success) {
|
|
295
|
-
return response.data
|
|
111
|
+
if (this._initialized) {
|
|
112
|
+
this.updateContext({ authToken: response.token })
|
|
296
113
|
}
|
|
297
|
-
throw new Error(response.message)
|
|
298
|
-
} catch (error) {
|
|
299
|
-
throw new Error(`Password reset confirmation failed: ${error.message}`, { cause: error })
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
114
|
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
body: JSON.stringify({ token }),
|
|
308
|
-
methodName: 'confirmRegistration'
|
|
115
|
+
based.setAuthState({
|
|
116
|
+
token: response.token,
|
|
117
|
+
userId: response.userId,
|
|
118
|
+
persistent: true
|
|
309
119
|
})
|
|
310
|
-
if (response.success) {
|
|
311
|
-
return response.data
|
|
312
|
-
}
|
|
313
|
-
throw new Error(response.message)
|
|
314
|
-
} catch (error) {
|
|
315
|
-
throw new Error(`Registration confirmation failed: ${error.message}`, { cause: error })
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
120
|
|
|
319
|
-
|
|
320
|
-
this._requireReady('requestPasswordChange')
|
|
321
|
-
try {
|
|
322
|
-
const response = await this._request('/auth/request-password-change', {
|
|
323
|
-
method: 'POST',
|
|
324
|
-
methodName: 'requestPasswordChange'
|
|
325
|
-
})
|
|
326
|
-
if (response.success) {
|
|
327
|
-
return response.data
|
|
328
|
-
}
|
|
329
|
-
throw new Error(response.message)
|
|
121
|
+
return response
|
|
330
122
|
} catch (error) {
|
|
331
|
-
throw new Error(`
|
|
123
|
+
throw new Error(`Google auth failed: ${error.message}`)
|
|
332
124
|
}
|
|
333
125
|
}
|
|
334
126
|
|
|
335
|
-
async
|
|
336
|
-
this._requireReady('confirmPasswordChange')
|
|
127
|
+
async googleAuthCallback (code, redirectUri) {
|
|
337
128
|
try {
|
|
338
|
-
const
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
129
|
+
const based = this._getBasedService('googleAuthCallback')
|
|
130
|
+
const response = await based.call('users:google-auth-callback', {
|
|
131
|
+
code,
|
|
132
|
+
redirectUri
|
|
342
133
|
})
|
|
343
|
-
|
|
344
|
-
|
|
134
|
+
|
|
135
|
+
if (this._initialized) {
|
|
136
|
+
this.updateContext({ authToken: response.token })
|
|
345
137
|
}
|
|
346
|
-
throw new Error(response.message)
|
|
347
|
-
} catch (error) {
|
|
348
|
-
throw new Error(`Password change confirmation failed: ${error.message}`, { cause: error })
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
138
|
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
const endpoint = session
|
|
357
|
-
? `/auth/me?session=${encodeURIComponent(session)}`
|
|
358
|
-
: '/auth/me'
|
|
359
|
-
|
|
360
|
-
const response = await this._request(endpoint, {
|
|
361
|
-
method: 'GET',
|
|
362
|
-
methodName: 'getMe'
|
|
139
|
+
based.setAuthState({
|
|
140
|
+
token: response.token,
|
|
141
|
+
userId: response.userId,
|
|
142
|
+
persistent: true
|
|
363
143
|
})
|
|
364
|
-
|
|
365
|
-
return response.data
|
|
366
|
-
}
|
|
367
|
-
throw new Error(response.message)
|
|
144
|
+
return response
|
|
368
145
|
} catch (error) {
|
|
369
|
-
throw new Error(`
|
|
146
|
+
throw new Error(`Google auth callback failed: ${error.message}`)
|
|
370
147
|
}
|
|
371
148
|
}
|
|
372
149
|
|
|
373
|
-
|
|
374
|
-
if (!this._tokenManager) {
|
|
375
|
-
return null
|
|
376
|
-
}
|
|
377
|
-
return this._tokenManager.getAccessToken()
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
/**
|
|
381
|
-
* Get stored authentication state (backward compatibility method)
|
|
382
|
-
* Replaces AuthService.getStoredAuthState()
|
|
383
|
-
*/
|
|
384
|
-
async getStoredAuthState() {
|
|
150
|
+
async githubAuth (code) {
|
|
385
151
|
try {
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
userId: false,
|
|
389
|
-
authToken: false
|
|
390
|
-
}
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
const tokenStatus = this._tokenManager.getTokenStatus()
|
|
394
|
-
|
|
395
|
-
if (!tokenStatus.hasTokens) {
|
|
396
|
-
return {
|
|
397
|
-
userId: false,
|
|
398
|
-
authToken: false
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
// If tokens exist but are invalid, try to refresh
|
|
403
|
-
if (!tokenStatus.isValid && tokenStatus.hasRefreshToken) {
|
|
404
|
-
try {
|
|
405
|
-
await this._tokenManager.ensureValidToken()
|
|
406
|
-
} catch (error) {
|
|
407
|
-
console.warn('[AuthService] Token refresh failed:', error.message)
|
|
408
|
-
// Only clear tokens if it's definitely an auth error, not a network error
|
|
409
|
-
if (
|
|
410
|
-
error.message.includes('401') ||
|
|
411
|
-
error.message.includes('403') ||
|
|
412
|
-
error.message.includes('invalid') ||
|
|
413
|
-
error.message.includes('expired')
|
|
414
|
-
) {
|
|
415
|
-
this._tokenManager.clearTokens()
|
|
416
|
-
return {
|
|
417
|
-
userId: false,
|
|
418
|
-
authToken: false,
|
|
419
|
-
error: `Authentication failed: ${error.message}`
|
|
420
|
-
}
|
|
421
|
-
}
|
|
422
|
-
// For network errors, keep tokens and return what we have
|
|
423
|
-
return {
|
|
424
|
-
userId: false,
|
|
425
|
-
authToken: this._tokenManager.getAccessToken(),
|
|
426
|
-
error: `Network error during token refresh: ${error.message}`,
|
|
427
|
-
hasTokens: true
|
|
428
|
-
}
|
|
429
|
-
}
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
// Check if we have a valid token now
|
|
433
|
-
const currentAccessToken = this._tokenManager.getAccessToken()
|
|
434
|
-
if (!currentAccessToken) {
|
|
435
|
-
return {
|
|
436
|
-
userId: false,
|
|
437
|
-
authToken: false
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
// Get current user data if we have valid tokens
|
|
442
|
-
// Be more lenient with API failures - don't immediately clear tokens
|
|
443
|
-
try {
|
|
444
|
-
const currentUser = await this.getMe()
|
|
445
|
-
|
|
446
|
-
return {
|
|
447
|
-
userId: currentUser.user.id,
|
|
448
|
-
authToken: currentAccessToken,
|
|
449
|
-
...currentUser,
|
|
450
|
-
error: null
|
|
451
|
-
}
|
|
452
|
-
} catch (error) {
|
|
453
|
-
console.warn('[AuthService] Failed to get user data:', error.message)
|
|
454
|
-
|
|
455
|
-
// Only clear tokens if it's an auth error (401, 403), not network errors
|
|
456
|
-
if (error.message.includes('401') || error.message.includes('403')) {
|
|
457
|
-
this._tokenManager.clearTokens()
|
|
458
|
-
return {
|
|
459
|
-
userId: false,
|
|
460
|
-
authToken: false,
|
|
461
|
-
error: `Authentication failed: ${error.message}`
|
|
462
|
-
}
|
|
463
|
-
}
|
|
152
|
+
const based = this._getBasedService('githubAuth')
|
|
153
|
+
const response = await based.call('users:github-auth', { code })
|
|
464
154
|
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
userId: false,
|
|
468
|
-
authToken: currentAccessToken,
|
|
469
|
-
error: `Failed to get user data: ${error.message}`,
|
|
470
|
-
hasTokens: true
|
|
471
|
-
}
|
|
472
|
-
}
|
|
473
|
-
} catch (error) {
|
|
474
|
-
console.error(
|
|
475
|
-
'[AuthService] Unexpected error in getStoredAuthState:',
|
|
476
|
-
error
|
|
477
|
-
)
|
|
478
|
-
return {
|
|
479
|
-
userId: false,
|
|
480
|
-
authToken: false,
|
|
481
|
-
error: `Failed to get stored auth state: ${error.message}`
|
|
155
|
+
if (this._initialized) {
|
|
156
|
+
this.updateContext({ authToken: response.token })
|
|
482
157
|
}
|
|
483
|
-
}
|
|
484
|
-
}
|
|
485
158
|
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
try {
|
|
491
|
-
const response = await this._request('/users/profile', {
|
|
492
|
-
method: 'GET',
|
|
493
|
-
methodName: 'getUserProfile'
|
|
159
|
+
based.setAuthState({
|
|
160
|
+
token: response.token,
|
|
161
|
+
userId: response.userId,
|
|
162
|
+
persistent: true
|
|
494
163
|
})
|
|
495
|
-
if (response.success) {
|
|
496
|
-
return response.data
|
|
497
|
-
}
|
|
498
|
-
throw new Error(response.message)
|
|
499
|
-
} catch (error) {
|
|
500
|
-
throw new Error(`Failed to get user profile: ${error.message}`, { cause: error })
|
|
501
|
-
}
|
|
502
|
-
}
|
|
503
164
|
|
|
504
|
-
|
|
505
|
-
this._requireReady('updateUserProfile')
|
|
506
|
-
try {
|
|
507
|
-
const response = await this._request('/users/profile', {
|
|
508
|
-
method: 'PATCH',
|
|
509
|
-
body: JSON.stringify(profileData),
|
|
510
|
-
methodName: 'updateUserProfile'
|
|
511
|
-
})
|
|
512
|
-
if (response.success) {
|
|
513
|
-
return response.data
|
|
514
|
-
}
|
|
515
|
-
throw new Error(response.message)
|
|
165
|
+
return response
|
|
516
166
|
} catch (error) {
|
|
517
|
-
throw new Error(`
|
|
167
|
+
throw new Error(`GitHub auth failed: ${error.message}`)
|
|
518
168
|
}
|
|
519
169
|
}
|
|
520
170
|
|
|
521
|
-
async
|
|
522
|
-
this._requireReady('
|
|
171
|
+
async logout () {
|
|
172
|
+
this._requireReady('logout')
|
|
523
173
|
try {
|
|
524
|
-
const
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
})
|
|
528
|
-
if (response.success) {
|
|
529
|
-
return response.data.map((project) => ({
|
|
530
|
-
...project,
|
|
531
|
-
...(project.icon && {
|
|
532
|
-
icon: {
|
|
533
|
-
src: `${this._apiUrl}/core/files/public/${project.icon.id}/download`,
|
|
534
|
-
...project.icon
|
|
535
|
-
}
|
|
536
|
-
})
|
|
537
|
-
}))
|
|
538
|
-
}
|
|
539
|
-
throw new Error(response.message)
|
|
174
|
+
const based = this._getBasedService('logout')
|
|
175
|
+
await based.call('users:logout')
|
|
176
|
+
this.updateContext({ authToken: null })
|
|
540
177
|
} catch (error) {
|
|
541
|
-
throw new Error(`
|
|
178
|
+
throw new Error(`Logout failed: ${error.message}`)
|
|
542
179
|
}
|
|
543
180
|
}
|
|
544
181
|
|
|
545
|
-
async
|
|
546
|
-
this._requireReady('
|
|
182
|
+
async updateUserRole (userId, newRole) {
|
|
183
|
+
this._requireReady('updateUserRole')
|
|
547
184
|
if (!userId) {
|
|
548
185
|
throw new Error('User ID is required')
|
|
549
186
|
}
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
method: 'GET',
|
|
553
|
-
methodName: 'getUser'
|
|
554
|
-
})
|
|
555
|
-
if (response.success) {
|
|
556
|
-
return response.data
|
|
557
|
-
}
|
|
558
|
-
throw new Error(response.message)
|
|
559
|
-
} catch (error) {
|
|
560
|
-
throw new Error(`Failed to get user: ${error.message}`, { cause: error })
|
|
561
|
-
}
|
|
562
|
-
}
|
|
563
|
-
|
|
564
|
-
async getUserByEmail(email) {
|
|
565
|
-
this._requireReady('getUserByEmail')
|
|
566
|
-
if (!email) {
|
|
567
|
-
throw new Error('Email is required')
|
|
187
|
+
if (!this._userRoles.has(newRole)) {
|
|
188
|
+
throw new Error(`Invalid role: ${newRole}`)
|
|
568
189
|
}
|
|
569
190
|
try {
|
|
570
|
-
const
|
|
571
|
-
|
|
572
|
-
methodName: 'getUserByEmail'
|
|
573
|
-
})
|
|
574
|
-
if (response.success) {
|
|
575
|
-
return response.data.user
|
|
576
|
-
}
|
|
577
|
-
throw new Error(response.message)
|
|
191
|
+
const based = this._getBasedService('updateUserRole')
|
|
192
|
+
return await based.call('users:update-role', { userId, role: newRole })
|
|
578
193
|
} catch (error) {
|
|
579
|
-
throw new Error(`Failed to
|
|
194
|
+
throw new Error(`Failed to update user role: ${error.message}`)
|
|
580
195
|
}
|
|
581
196
|
}
|
|
582
197
|
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
/**
|
|
586
|
-
* Get the current user's role for a specific project by project ID
|
|
587
|
-
* Uses caching to avoid repeated API calls
|
|
588
|
-
*/
|
|
589
|
-
async getMyProjectRole(projectId) {
|
|
590
|
-
this._requireReady('getMyProjectRole')
|
|
198
|
+
async updateProjectTier (projectId, newTier) {
|
|
199
|
+
this._requireReady('updateProjectTier')
|
|
591
200
|
if (!projectId) {
|
|
592
201
|
throw new Error('Project ID is required')
|
|
593
202
|
}
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
if (!this.hasValidTokens()) {
|
|
597
|
-
return 'guest'
|
|
203
|
+
if (!this._projectTiers.has(newTier)) {
|
|
204
|
+
throw new Error(`Invalid project tier: ${newTier}`)
|
|
598
205
|
}
|
|
599
|
-
|
|
600
|
-
// Check cache first
|
|
601
|
-
const cacheKey = `role_${projectId}`
|
|
602
|
-
const cached = this._projectRoleCache.get(cacheKey)
|
|
603
|
-
|
|
604
|
-
if (cached && Date.now() - cached.timestamp < this._roleCacheExpiry) {
|
|
605
|
-
return cached.role
|
|
606
|
-
}
|
|
607
|
-
|
|
608
206
|
try {
|
|
609
|
-
const
|
|
610
|
-
|
|
611
|
-
|
|
207
|
+
const based = this._getBasedService('updateProjectTier')
|
|
208
|
+
return await based.call('projects:update-tier', {
|
|
209
|
+
projectId,
|
|
210
|
+
tier: newTier
|
|
612
211
|
})
|
|
613
|
-
|
|
614
|
-
if (response.success) {
|
|
615
|
-
const { role } = response.data
|
|
616
|
-
// Cache the result
|
|
617
|
-
this._projectRoleCache.set(cacheKey, {
|
|
618
|
-
role,
|
|
619
|
-
timestamp: Date.now()
|
|
620
|
-
})
|
|
621
|
-
return role
|
|
622
|
-
}
|
|
623
|
-
throw new Error(response.message)
|
|
624
212
|
} catch (error) {
|
|
625
|
-
|
|
626
|
-
// If request failed due to missing/invalid auth, default to guest
|
|
627
|
-
if (/401|403|unauthorized|no token|invalid token/iu.test(message)) {
|
|
628
|
-
return 'guest'
|
|
629
|
-
}
|
|
630
|
-
throw new Error(`Failed to get project role: ${message}`, { cause: error })
|
|
213
|
+
throw new Error(`Failed to update project tier: ${error.message}`)
|
|
631
214
|
}
|
|
632
215
|
}
|
|
633
216
|
|
|
634
|
-
|
|
635
|
-
* Get the current user's role for a specific project by project key
|
|
636
|
-
* Uses caching to avoid repeated API calls
|
|
637
|
-
*/
|
|
638
|
-
async getMyProjectRoleByKey(projectKey) {
|
|
639
|
-
this._requireReady('getMyProjectRoleByKey')
|
|
640
|
-
if (!projectKey) {
|
|
641
|
-
throw new Error('Project key is required')
|
|
642
|
-
}
|
|
643
|
-
|
|
644
|
-
// If there are no valid tokens, treat user as guest for public access
|
|
645
|
-
if (!this.hasValidTokens()) {
|
|
646
|
-
return 'guest'
|
|
647
|
-
}
|
|
648
|
-
|
|
649
|
-
// Check cache first
|
|
650
|
-
const cacheKey = `role_key_${projectKey}`
|
|
651
|
-
const cached = this._projectRoleCache.get(cacheKey)
|
|
652
|
-
|
|
653
|
-
if (cached && Date.now() - cached.timestamp < this._roleCacheExpiry) {
|
|
654
|
-
return cached.role
|
|
655
|
-
}
|
|
656
|
-
|
|
657
|
-
try {
|
|
658
|
-
const response = await this._request(`/projects/key/${projectKey}/role`, {
|
|
659
|
-
method: 'GET',
|
|
660
|
-
methodName: 'getMyProjectRoleByKey'
|
|
661
|
-
})
|
|
662
|
-
|
|
663
|
-
if (response.success) {
|
|
664
|
-
const { role } = response.data
|
|
665
|
-
// Cache the result
|
|
666
|
-
this._projectRoleCache.set(cacheKey, {
|
|
667
|
-
role,
|
|
668
|
-
timestamp: Date.now()
|
|
669
|
-
})
|
|
670
|
-
return role
|
|
671
|
-
}
|
|
672
|
-
throw new Error(response.message)
|
|
673
|
-
} catch (error) {
|
|
674
|
-
const message = error?.message || ''
|
|
675
|
-
// If request failed due to missing/invalid auth, default to guest
|
|
676
|
-
if (/401|403|unauthorized|no token|invalid token/iu.test(message)) {
|
|
677
|
-
return 'guest'
|
|
678
|
-
}
|
|
679
|
-
throw new Error(`Failed to get project role by key: ${message}`, { cause: error })
|
|
680
|
-
}
|
|
681
|
-
}
|
|
682
|
-
|
|
683
|
-
/**
|
|
684
|
-
* Clear the project role cache for a specific project or all projects
|
|
685
|
-
*/
|
|
686
|
-
clearProjectRoleCache(projectId = null) {
|
|
687
|
-
if (projectId) {
|
|
688
|
-
// Clear specific project cache
|
|
689
|
-
this._projectRoleCache.delete(`role_${projectId}`)
|
|
690
|
-
// Also clear by key if we have it cached
|
|
691
|
-
for (const [key] of this._projectRoleCache) {
|
|
692
|
-
if (key.startsWith('role_key_')) {
|
|
693
|
-
this._projectRoleCache.delete(key)
|
|
694
|
-
}
|
|
695
|
-
}
|
|
696
|
-
} else {
|
|
697
|
-
// Clear all cache
|
|
698
|
-
this._projectRoleCache.clear()
|
|
699
|
-
}
|
|
700
|
-
}
|
|
701
|
-
|
|
702
|
-
/**
|
|
703
|
-
* Get project role with fallback to user projects list
|
|
704
|
-
* This method tries to get the role from user projects first,
|
|
705
|
-
* then falls back to API call if not found
|
|
706
|
-
*/
|
|
707
|
-
async getProjectRoleWithFallback(projectId, userProjects = null) {
|
|
708
|
-
this._requireReady('getProjectRoleWithFallback')
|
|
709
|
-
if (!projectId) {
|
|
710
|
-
throw new Error('Project ID is required')
|
|
711
|
-
}
|
|
712
|
-
|
|
713
|
-
// First try to find in user projects if provided
|
|
714
|
-
if (userProjects && Array.isArray(userProjects)) {
|
|
715
|
-
const userProject = userProjects.find(p => p.id === projectId)
|
|
716
|
-
if (userProject && userProject.role) {
|
|
717
|
-
return userProject.role
|
|
718
|
-
}
|
|
719
|
-
}
|
|
720
|
-
|
|
721
|
-
// Fallback to API call
|
|
722
|
-
return await this.getMyProjectRole(projectId)
|
|
723
|
-
}
|
|
724
|
-
|
|
725
|
-
/**
|
|
726
|
-
* Get project role with fallback to user projects list (by project key)
|
|
727
|
-
* This method tries to get the role from user projects first,
|
|
728
|
-
* then falls back to API call if not found
|
|
729
|
-
*/
|
|
730
|
-
async getProjectRoleByKeyWithFallback(projectKey, userProjects = null) {
|
|
731
|
-
this._requireReady('getProjectRoleByKeyWithFallback')
|
|
732
|
-
if (!projectKey) {
|
|
733
|
-
throw new Error('Project key is required')
|
|
734
|
-
}
|
|
735
|
-
|
|
736
|
-
// First try to find in user projects if provided
|
|
737
|
-
if (userProjects && Array.isArray(userProjects)) {
|
|
738
|
-
const userProject = userProjects.find(p => p.key === projectKey)
|
|
739
|
-
if (userProject && userProject.role) {
|
|
740
|
-
return userProject.role
|
|
741
|
-
}
|
|
742
|
-
}
|
|
743
|
-
|
|
744
|
-
// Fallback to API call
|
|
745
|
-
return await this.getMyProjectRoleByKey(projectKey)
|
|
746
|
-
}
|
|
747
|
-
|
|
748
|
-
// ==================== AUTH HELPER METHODS ====================
|
|
749
|
-
|
|
750
|
-
/**
|
|
751
|
-
* Debug method to check token status
|
|
752
|
-
*/
|
|
753
|
-
getTokenDebugInfo() {
|
|
754
|
-
if (!this._tokenManager) {
|
|
755
|
-
return {
|
|
756
|
-
tokenManagerExists: false,
|
|
757
|
-
error: 'TokenManager not initialized'
|
|
758
|
-
}
|
|
759
|
-
}
|
|
760
|
-
|
|
761
|
-
const tokenStatus = this._tokenManager.getTokenStatus()
|
|
762
|
-
const { tokens } = this._tokenManager
|
|
763
|
-
|
|
764
|
-
return {
|
|
765
|
-
tokenManagerExists: true,
|
|
766
|
-
tokenStatus,
|
|
767
|
-
hasAccessToken: Boolean(tokens.accessToken),
|
|
768
|
-
hasRefreshToken: Boolean(tokens.refreshToken),
|
|
769
|
-
accessTokenPreview: tokens.accessToken
|
|
770
|
-
? `${tokens.accessToken.substring(0, 20)}...`
|
|
771
|
-
: null,
|
|
772
|
-
expiresAt: tokens.expiresAt,
|
|
773
|
-
timeToExpiry: tokenStatus.timeToExpiry,
|
|
774
|
-
authHeader: this._tokenManager.getAuthHeader()
|
|
775
|
-
}
|
|
776
|
-
}
|
|
777
|
-
|
|
778
|
-
/**
|
|
779
|
-
* Helper method to check if user is authenticated
|
|
780
|
-
*/
|
|
781
|
-
isAuthenticated() {
|
|
782
|
-
if (!this._tokenManager) {
|
|
783
|
-
return false
|
|
784
|
-
}
|
|
785
|
-
return this._tokenManager.hasTokens()
|
|
786
|
-
}
|
|
787
|
-
|
|
788
|
-
/**
|
|
789
|
-
* Helper method to check if user has valid tokens
|
|
790
|
-
*/
|
|
791
|
-
hasValidTokens() {
|
|
792
|
-
if (!this._tokenManager) {
|
|
793
|
-
return false
|
|
794
|
-
}
|
|
795
|
-
return (
|
|
796
|
-
this._tokenManager.hasTokens() && this._tokenManager.isAccessTokenValid()
|
|
797
|
-
)
|
|
798
|
-
}
|
|
799
|
-
|
|
800
|
-
/**
|
|
801
|
-
* Helper method to get current user info
|
|
802
|
-
*/
|
|
803
|
-
async getCurrentUser() {
|
|
804
|
-
try {
|
|
805
|
-
return await this.getMe()
|
|
806
|
-
} catch (error) {
|
|
807
|
-
throw new Error(`Failed to get current user: ${error.message}`, { cause: error })
|
|
808
|
-
}
|
|
809
|
-
}
|
|
810
|
-
|
|
811
|
-
/**
|
|
812
|
-
* Helper method to validate user data for registration
|
|
813
|
-
*/
|
|
814
|
-
validateRegistrationData(userData) {
|
|
815
|
-
const errors = []
|
|
816
|
-
|
|
817
|
-
if (!userData.email || typeof userData.email !== 'string') {
|
|
818
|
-
errors.push('Email is required and must be a string')
|
|
819
|
-
} else if (!this._isValidEmail(userData.email)) {
|
|
820
|
-
errors.push('Email must be a valid email address')
|
|
821
|
-
}
|
|
822
|
-
|
|
823
|
-
if (!userData.password || typeof userData.password !== 'string') {
|
|
824
|
-
errors.push('Password is required and must be a string')
|
|
825
|
-
} else if (userData.password.length < 8) {
|
|
826
|
-
errors.push('Password must be at least 8 characters long')
|
|
827
|
-
}
|
|
828
|
-
|
|
829
|
-
if (userData.username && typeof userData.username !== 'string') {
|
|
830
|
-
errors.push('Username must be a string')
|
|
831
|
-
} else if (userData.username && userData.username.length < 3) {
|
|
832
|
-
errors.push('Username must be at least 3 characters long')
|
|
833
|
-
}
|
|
834
|
-
|
|
835
|
-
return {
|
|
836
|
-
isValid: errors.length === 0,
|
|
837
|
-
errors
|
|
838
|
-
}
|
|
839
|
-
}
|
|
840
|
-
|
|
841
|
-
/**
|
|
842
|
-
* Helper method to register with validation
|
|
843
|
-
*/
|
|
844
|
-
async registerWithValidation(userData, options = {}) {
|
|
845
|
-
const validation = this.validateRegistrationData(userData)
|
|
846
|
-
if (!validation.isValid) {
|
|
847
|
-
throw new Error(`Validation failed: ${validation.errors.join(', ')}`)
|
|
848
|
-
}
|
|
849
|
-
|
|
850
|
-
return await this.register(userData, options)
|
|
851
|
-
}
|
|
852
|
-
|
|
853
|
-
/**
|
|
854
|
-
* Helper method to login with validation
|
|
855
|
-
*/
|
|
856
|
-
async loginWithValidation(email, password, options = {}) {
|
|
857
|
-
if (!email || typeof email !== 'string') {
|
|
858
|
-
throw new Error('Email is required and must be a string')
|
|
859
|
-
}
|
|
860
|
-
|
|
861
|
-
if (!password || typeof password !== 'string') {
|
|
862
|
-
throw new Error('Password is required and must be a string')
|
|
863
|
-
}
|
|
864
|
-
|
|
865
|
-
if (!this._isValidEmail(email)) {
|
|
866
|
-
throw new Error('Email must be a valid email address')
|
|
867
|
-
}
|
|
868
|
-
|
|
869
|
-
return await this.login(email, password, options)
|
|
870
|
-
}
|
|
871
|
-
|
|
872
|
-
/**
|
|
873
|
-
* Private helper to validate email format
|
|
874
|
-
*/
|
|
875
|
-
_isValidEmail(email) {
|
|
876
|
-
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/u
|
|
877
|
-
return emailRegex.test(email)
|
|
878
|
-
}
|
|
879
|
-
|
|
880
|
-
// ==================== PERMISSION METHODS (Existing) ====================
|
|
881
|
-
|
|
882
|
-
hasPermission(requiredPermission) {
|
|
217
|
+
hasPermission (requiredPermission) {
|
|
883
218
|
const authState = this._context?.state
|
|
884
219
|
if (!authState) {
|
|
885
220
|
return false
|
|
@@ -895,21 +230,21 @@ export class AuthService extends BaseService {
|
|
|
895
230
|
)
|
|
896
231
|
}
|
|
897
232
|
|
|
898
|
-
hasGlobalPermission(globalRole, requiredPermission) {
|
|
233
|
+
hasGlobalPermission (globalRole, requiredPermission) {
|
|
899
234
|
return ROLE_PERMISSIONS[globalRole]?.includes(requiredPermission) || false
|
|
900
235
|
}
|
|
901
236
|
|
|
902
|
-
checkProjectPermission(projectRole, requiredPermission) {
|
|
237
|
+
checkProjectPermission (projectRole, requiredPermission) {
|
|
903
238
|
return (
|
|
904
239
|
PROJECT_ROLE_PERMISSIONS[projectRole]?.includes(requiredPermission) ||
|
|
905
240
|
false
|
|
906
241
|
)
|
|
907
242
|
}
|
|
908
243
|
|
|
909
|
-
checkProjectFeature(projectTier, feature) {
|
|
244
|
+
checkProjectFeature (projectTier, feature) {
|
|
910
245
|
if (feature.startsWith('aiCopilot') || feature.startsWith('aiChatbot')) {
|
|
911
246
|
const [featureBase] = feature.split(':')
|
|
912
|
-
const tierFeature = TIER_FEATURES[projectTier]?.find(
|
|
247
|
+
const tierFeature = TIER_FEATURES[projectTier]?.find(f =>
|
|
913
248
|
f.startsWith(featureBase)
|
|
914
249
|
)
|
|
915
250
|
if (!tierFeature) {
|
|
@@ -923,7 +258,7 @@ export class AuthService extends BaseService {
|
|
|
923
258
|
}
|
|
924
259
|
|
|
925
260
|
// Operation checking
|
|
926
|
-
async canPerformOperation(projectId, operation, options = {}) {
|
|
261
|
+
async canPerformOperation (projectId, operation, options = {}) {
|
|
927
262
|
this._requireReady()
|
|
928
263
|
if (!projectId) {
|
|
929
264
|
throw new Error('Project ID is required')
|
|
@@ -935,14 +270,17 @@ export class AuthService extends BaseService {
|
|
|
935
270
|
if (!operationConfig) {
|
|
936
271
|
return false
|
|
937
272
|
}
|
|
273
|
+
if (!operationConfig) {
|
|
274
|
+
return false
|
|
275
|
+
}
|
|
938
276
|
|
|
939
277
|
const { permissions = [], features = [] } = operationConfig
|
|
940
278
|
|
|
941
279
|
try {
|
|
942
280
|
// Check permissions
|
|
943
281
|
const permissionResults = await Promise.all(
|
|
944
|
-
permissions.map(
|
|
945
|
-
this.
|
|
282
|
+
permissions.map(permission =>
|
|
283
|
+
this.hasProjectPermission(projectId, permission)
|
|
946
284
|
)
|
|
947
285
|
)
|
|
948
286
|
|
|
@@ -953,10 +291,13 @@ export class AuthService extends BaseService {
|
|
|
953
291
|
if (!hasPermissions) {
|
|
954
292
|
return false
|
|
955
293
|
}
|
|
294
|
+
if (!hasPermissions) {
|
|
295
|
+
return false
|
|
296
|
+
}
|
|
956
297
|
|
|
957
298
|
// Check features if required
|
|
958
299
|
if (checkFeatures && features.length > 0) {
|
|
959
|
-
const featureResults = features.map(
|
|
300
|
+
const featureResults = features.map(feature => {
|
|
960
301
|
const result = this.hasProjectFeature(projectId, feature)
|
|
961
302
|
return feature.includes(':')
|
|
962
303
|
? typeof result === 'number' && result > 0
|
|
@@ -970,6 +311,9 @@ export class AuthService extends BaseService {
|
|
|
970
311
|
if (!hasFeatures) {
|
|
971
312
|
return false
|
|
972
313
|
}
|
|
314
|
+
if (!hasFeatures) {
|
|
315
|
+
return false
|
|
316
|
+
}
|
|
973
317
|
}
|
|
974
318
|
|
|
975
319
|
return true
|
|
@@ -980,7 +324,7 @@ export class AuthService extends BaseService {
|
|
|
980
324
|
}
|
|
981
325
|
|
|
982
326
|
// Higher-level permission methods
|
|
983
|
-
async withPermission(projectId, operation, action) {
|
|
327
|
+
async withPermission (projectId, operation, action) {
|
|
984
328
|
this._requireReady()
|
|
985
329
|
if (!projectId) {
|
|
986
330
|
throw new Error('Project ID is required')
|
|
@@ -996,115 +340,225 @@ export class AuthService extends BaseService {
|
|
|
996
340
|
return action()
|
|
997
341
|
}
|
|
998
342
|
|
|
999
|
-
//
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
343
|
+
// Project access information
|
|
344
|
+
async getProjectAccess (projectId) {
|
|
345
|
+
this._requireReady()
|
|
346
|
+
if (!projectId) {
|
|
347
|
+
throw new Error('Project ID is required')
|
|
1004
348
|
}
|
|
1005
|
-
// Clear project role cache
|
|
1006
|
-
this._projectRoleCache.clear()
|
|
1007
|
-
this._setReady(false)
|
|
1008
|
-
}
|
|
1009
349
|
|
|
1010
|
-
|
|
1011
|
-
const target =
|
|
1012
|
-
payload && typeof payload === 'object'
|
|
1013
|
-
? { ...payload }
|
|
1014
|
-
: {}
|
|
350
|
+
const operations = Object.keys(PERMISSION_MAP)
|
|
1015
351
|
|
|
1016
|
-
const
|
|
352
|
+
const access = await Promise.all(
|
|
353
|
+
operations.map(async operation => {
|
|
354
|
+
const allowed = await this.canPerformOperation(projectId, operation)
|
|
355
|
+
const config = PERMISSION_MAP[operation]
|
|
1017
356
|
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
357
|
+
return {
|
|
358
|
+
operation,
|
|
359
|
+
allowed,
|
|
360
|
+
permissions: config.permissions,
|
|
361
|
+
features: config.features,
|
|
362
|
+
aiTokens: operation.startsWith('ai')
|
|
363
|
+
? this._getAITokens(projectId, operation.replace('ai', ''))
|
|
364
|
+
: null
|
|
365
|
+
}
|
|
366
|
+
})
|
|
367
|
+
)
|
|
1022
368
|
|
|
1023
|
-
return {
|
|
369
|
+
return {
|
|
370
|
+
projectId,
|
|
371
|
+
permissions: access.reduce(
|
|
372
|
+
(acc, { operation, ...details }) => ({
|
|
373
|
+
...acc,
|
|
374
|
+
[operation]: details
|
|
375
|
+
}),
|
|
376
|
+
{}
|
|
377
|
+
),
|
|
378
|
+
timestamp: new Date().toISOString()
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
// AI token management
|
|
383
|
+
_getAITokens (projectId, featureType) {
|
|
384
|
+
const tokenFeatures = [
|
|
385
|
+
`ai${featureType}:3`,
|
|
386
|
+
`ai${featureType}:5`,
|
|
387
|
+
`ai${featureType}:15`
|
|
388
|
+
]
|
|
389
|
+
|
|
390
|
+
return tokenFeatures.reduce((total, feature) => {
|
|
391
|
+
const tokens = this.hasProjectFeature(projectId, feature)
|
|
392
|
+
return total + (typeof tokens === 'number' ? tokens : 0)
|
|
393
|
+
}, 0)
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
async getProjectMembers (projectId) {
|
|
397
|
+
this._requireReady('getProjectMembers')
|
|
398
|
+
if (!projectId) {
|
|
399
|
+
throw new Error('Project ID is required')
|
|
400
|
+
}
|
|
401
|
+
try {
|
|
402
|
+
const based = this._getBasedService('getProjectMembers')
|
|
403
|
+
return await based.call('projects:get-members', { projectId })
|
|
404
|
+
} catch (error) {
|
|
405
|
+
if (error.message?.includes('Authentication failed. Please try again'))
|
|
406
|
+
window.location.reload()
|
|
407
|
+
throw new Error(`Failed to get project members: ${error.message}`)
|
|
408
|
+
}
|
|
1024
409
|
}
|
|
1025
410
|
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
411
|
+
async inviteMember (projectId, email, role, name, callbackUrl) {
|
|
412
|
+
this._requireReady('inviteMember')
|
|
413
|
+
if (!projectId) {
|
|
414
|
+
throw new Error('Project ID is required')
|
|
1029
415
|
}
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
return this._pluginSession
|
|
416
|
+
if (!email) {
|
|
417
|
+
throw new Error('Email is required')
|
|
1033
418
|
}
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
if (optionSession) {
|
|
1037
|
-
return this._cachePluginSession(optionSession)
|
|
419
|
+
if (!callbackUrl || Object.keys(callbackUrl).length === 0) {
|
|
420
|
+
throw new Error('Callback Url is required')
|
|
1038
421
|
}
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
if (contextSession) {
|
|
1042
|
-
return this._cachePluginSession(contextSession)
|
|
422
|
+
if (!role || !this._userRoles.has(role)) {
|
|
423
|
+
throw new Error(`Invalid role: ${role}`)
|
|
1043
424
|
}
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
try {
|
|
1056
|
-
if (window.localStorage) {
|
|
1057
|
-
const stored = window.localStorage.getItem(PLUGIN_SESSION_STORAGE_KEY)
|
|
1058
|
-
if (stored) {
|
|
1059
|
-
this._pluginSession = stored
|
|
1060
|
-
return stored
|
|
1061
|
-
}
|
|
1062
|
-
}
|
|
1063
|
-
} catch {
|
|
1064
|
-
// Ignore storage access issues
|
|
1065
|
-
}
|
|
425
|
+
try {
|
|
426
|
+
const based = this._getBasedService('inviteMember')
|
|
427
|
+
return await based.call('projects:invite-member', {
|
|
428
|
+
projectId,
|
|
429
|
+
email,
|
|
430
|
+
role,
|
|
431
|
+
name,
|
|
432
|
+
callbackUrl
|
|
433
|
+
})
|
|
434
|
+
} catch (error) {
|
|
435
|
+
throw new Error(`Failed to invite member: ${error.message}`)
|
|
1066
436
|
}
|
|
1067
|
-
|
|
1068
|
-
return null
|
|
1069
437
|
}
|
|
1070
438
|
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
439
|
+
async acceptInvite (token) {
|
|
440
|
+
this._requireReady('acceptInvite')
|
|
441
|
+
try {
|
|
442
|
+
const based = this._getBasedService('acceptInvite')
|
|
443
|
+
return await based.call('projects:accept-invite', { token })
|
|
444
|
+
} catch (error) {
|
|
445
|
+
throw new Error(`Failed to accept invite: ${error.message}`)
|
|
1074
446
|
}
|
|
447
|
+
}
|
|
1075
448
|
|
|
1076
|
-
|
|
449
|
+
async updateMemberRole (projectId, userId, role) {
|
|
450
|
+
this._requireReady('updateMemberRole')
|
|
451
|
+
if (!projectId) {
|
|
452
|
+
throw new Error('Project ID is required')
|
|
453
|
+
}
|
|
454
|
+
if (!userId) {
|
|
455
|
+
throw new Error('User ID is required')
|
|
456
|
+
}
|
|
457
|
+
if (!this._userRoles.has(role)) {
|
|
458
|
+
throw new Error(`Invalid role: ${role}`)
|
|
459
|
+
}
|
|
460
|
+
try {
|
|
461
|
+
const based = this._getBasedService('updateMemberRole')
|
|
462
|
+
return await based.call('projects:update-member-role', {
|
|
463
|
+
projectId,
|
|
464
|
+
userId,
|
|
465
|
+
role
|
|
466
|
+
})
|
|
467
|
+
} catch (error) {
|
|
468
|
+
throw new Error(`Failed to update member role: ${error.message}`)
|
|
469
|
+
}
|
|
470
|
+
}
|
|
1077
471
|
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
}
|
|
472
|
+
async removeMember (projectId, userId) {
|
|
473
|
+
this._requireReady('removeMember')
|
|
474
|
+
if (!projectId || !userId) {
|
|
475
|
+
throw new Error('Project ID and user ID are required')
|
|
476
|
+
}
|
|
477
|
+
try {
|
|
478
|
+
const based = this._getBasedService('removeMember')
|
|
479
|
+
return await based.call('projects:remove-member', { projectId, userId })
|
|
480
|
+
} catch (error) {
|
|
481
|
+
throw new Error(`Failed to remove member: ${error.message}`)
|
|
1086
482
|
}
|
|
483
|
+
}
|
|
1087
484
|
|
|
1088
|
-
|
|
485
|
+
async confirmRegistration (token) {
|
|
486
|
+
try {
|
|
487
|
+
const based = this._getBasedService('confirmRegistration')
|
|
488
|
+
return await based.call('users:register-confirmation', { token })
|
|
489
|
+
} catch (error) {
|
|
490
|
+
throw new Error(`Registration confirmation failed: ${error.message}`)
|
|
491
|
+
}
|
|
1089
492
|
}
|
|
1090
493
|
|
|
1091
|
-
|
|
1092
|
-
|
|
494
|
+
async requestPasswordReset (email, callbackUrl) {
|
|
495
|
+
try {
|
|
496
|
+
const based = this._getBasedService('requestPasswordReset')
|
|
497
|
+
return await based.call('users:reset-password', { email, callbackUrl })
|
|
498
|
+
} catch (error) {
|
|
499
|
+
throw new Error(`Password reset request failed: ${error.message}`)
|
|
500
|
+
}
|
|
1093
501
|
}
|
|
1094
502
|
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
503
|
+
async confirmPasswordReset (token, newPassword) {
|
|
504
|
+
try {
|
|
505
|
+
const based = this._getBasedService('confirmPasswordReset')
|
|
506
|
+
return await based.call('users:reset-password-confirm', {
|
|
507
|
+
token,
|
|
508
|
+
newPassword
|
|
509
|
+
})
|
|
510
|
+
} catch (error) {
|
|
511
|
+
throw new Error(`Password reset confirmation failed: ${error.message}`)
|
|
1098
512
|
}
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
async getStoredAuthState () {
|
|
516
|
+
try {
|
|
517
|
+
const based = this._getBasedService('getStoredAuthState')
|
|
518
|
+
const { authState } = based
|
|
1099
519
|
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
520
|
+
if (authState?.token) {
|
|
521
|
+
return {
|
|
522
|
+
userId: authState.userId,
|
|
523
|
+
authToken: authState.token,
|
|
524
|
+
projectRoles: authState.projectRoles,
|
|
525
|
+
globalRole: authState.globalRole,
|
|
526
|
+
error: null
|
|
1104
527
|
}
|
|
1105
|
-
}
|
|
1106
|
-
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
return {
|
|
531
|
+
userId: false,
|
|
532
|
+
authToken: false
|
|
533
|
+
}
|
|
534
|
+
} catch (error) {
|
|
535
|
+
this._setError(error)
|
|
536
|
+
return {
|
|
537
|
+
userId: false,
|
|
538
|
+
authToken: false,
|
|
539
|
+
error: `Failed to get stored auth state: ${error.message}`
|
|
1107
540
|
}
|
|
1108
541
|
}
|
|
1109
542
|
}
|
|
543
|
+
|
|
544
|
+
async subscribeToAuthChanges (callback) {
|
|
545
|
+
const based = this._getBasedService('subscribeToAuthChanges')
|
|
546
|
+
based.on('authstate-change', async authState => {
|
|
547
|
+
const formattedState = authState?.token
|
|
548
|
+
? {
|
|
549
|
+
userId: authState.userId,
|
|
550
|
+
authToken: authState.token,
|
|
551
|
+
projectRoles: authState.projectRoles,
|
|
552
|
+
globalRole: authState.globalRole,
|
|
553
|
+
error: null
|
|
554
|
+
}
|
|
555
|
+
: {
|
|
556
|
+
userId: false,
|
|
557
|
+
authToken: false
|
|
558
|
+
}
|
|
559
|
+
await callback(formattedState)
|
|
560
|
+
})
|
|
561
|
+
|
|
562
|
+
return () => based.off('authstate-change')
|
|
563
|
+
}
|
|
1110
564
|
}
|