@symbo.ls/sdk 3.1.1 → 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 +172 -11
- package/dist/cjs/config/environment.js +39 -33
- package/dist/cjs/index.js +35 -8
- package/dist/cjs/services/AuthService.js +44 -3
- package/dist/cjs/services/BasedService.js +530 -24
- package/dist/cjs/services/CoreService.js +1751 -0
- package/dist/cjs/services/SocketIOService.js +34 -36
- package/dist/cjs/services/SymstoryService.js +135 -49
- package/dist/cjs/services/index.js +4 -4
- package/dist/cjs/utils/TokenManager.js +374 -0
- package/dist/cjs/utils/basedQuerys.js +120 -0
- package/dist/cjs/utils/permission.js +4 -4
- package/dist/cjs/utils/services.js +32 -9
- package/dist/cjs/utils/symstoryClient.js +32 -1
- package/dist/esm/config/environment.js +39 -33
- package/dist/esm/index.js +8964 -11076
- package/dist/esm/services/AuthService.js +48 -7
- package/dist/esm/services/BasedService.js +683 -56
- package/dist/esm/services/CoreService.js +2264 -0
- package/dist/esm/services/SocketIOService.js +71 -68
- package/dist/esm/services/SymstoryService.js +293 -101
- package/dist/esm/services/index.js +8905 -11066
- package/dist/esm/utils/TokenManager.js +360 -0
- package/dist/esm/utils/basedQuerys.js +120 -0
- package/dist/esm/utils/permission.js +4 -4
- package/dist/esm/utils/services.js +32 -9
- package/dist/esm/utils/symstoryClient.js +69 -33
- package/dist/esm/utils/validation.js +89 -19
- package/dist/node/config/environment.js +39 -33
- package/dist/node/index.js +43 -10
- package/dist/node/services/AuthService.js +44 -3
- package/dist/node/services/BasedService.js +531 -25
- package/dist/node/services/CoreService.js +1722 -0
- package/dist/node/services/SocketIOService.js +34 -36
- package/dist/node/services/SymstoryService.js +135 -49
- package/dist/node/services/index.js +4 -4
- package/dist/node/utils/TokenManager.js +355 -0
- package/dist/node/utils/basedQuerys.js +120 -0
- package/dist/node/utils/permission.js +4 -4
- package/dist/node/utils/services.js +32 -9
- package/dist/node/utils/symstoryClient.js +32 -1
- package/package.json +16 -13
- package/src/config/environment.js +40 -35
- package/src/index.js +49 -10
- package/src/services/AuthService.js +52 -3
- package/src/services/BasedService.js +602 -23
- package/src/services/CoreService.js +1943 -0
- package/src/services/SocketIOService.js +49 -71
- package/src/services/SymstoryService.js +150 -64
- package/src/services/index.js +4 -4
- package/src/utils/TokenManager.js +424 -0
- package/src/utils/basedQuerys.js +123 -0
- package/src/utils/permission.js +4 -4
- package/src/utils/services.js +32 -9
- package/src/utils/symstoryClient.js +35 -1
|
@@ -0,0 +1,424 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TokenManager - Handles access and refresh token management
|
|
3
|
+
* Provides persistence, automatic refresh, and token lifecycle management
|
|
4
|
+
*/
|
|
5
|
+
export class TokenManager {
|
|
6
|
+
constructor (options = {}) {
|
|
7
|
+
this.config = {
|
|
8
|
+
storagePrefix: 'symbols_',
|
|
9
|
+
storageType: 'localStorage', // 'localStorage' | 'sessionStorage' | 'memory'
|
|
10
|
+
refreshBuffer: 60 * 1000, // Refresh 1 minute before expiry
|
|
11
|
+
maxRetries: 3,
|
|
12
|
+
apiUrl: options.apiUrl || '/api',
|
|
13
|
+
onTokenRefresh: options.onTokenRefresh || null,
|
|
14
|
+
onTokenExpired: options.onTokenExpired || null,
|
|
15
|
+
onTokenError: options.onTokenError || null,
|
|
16
|
+
...options
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
this.tokens = {
|
|
20
|
+
accessToken: null,
|
|
21
|
+
refreshToken: null,
|
|
22
|
+
expiresAt: null,
|
|
23
|
+
expiresIn: null
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
this.refreshPromise = null
|
|
27
|
+
this.refreshTimeout = null
|
|
28
|
+
this.retryCount = 0
|
|
29
|
+
|
|
30
|
+
// Load tokens from storage on initialization
|
|
31
|
+
this.loadTokens()
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Storage keys
|
|
36
|
+
*/
|
|
37
|
+
get storageKeys () {
|
|
38
|
+
return {
|
|
39
|
+
accessToken: `${this.config.storagePrefix}access_token`,
|
|
40
|
+
refreshToken: `${this.config.storagePrefix}refresh_token`,
|
|
41
|
+
expiresAt: `${this.config.storagePrefix}expires_at`,
|
|
42
|
+
expiresIn: `${this.config.storagePrefix}expires_in`
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Get storage instance based on configuration
|
|
48
|
+
*/
|
|
49
|
+
get storage () {
|
|
50
|
+
if (typeof window === 'undefined') {
|
|
51
|
+
// Node.js environment - use memory storage
|
|
52
|
+
return this._memoryStorage
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
switch (this.config.storageType) {
|
|
56
|
+
case 'sessionStorage':
|
|
57
|
+
return window.sessionStorage
|
|
58
|
+
case 'memory':
|
|
59
|
+
return this._memoryStorage
|
|
60
|
+
default:
|
|
61
|
+
return window.localStorage
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Memory storage fallback for server-side rendering
|
|
67
|
+
*/
|
|
68
|
+
_memoryStorage = {
|
|
69
|
+
_data: {},
|
|
70
|
+
getItem: (key) => this._memoryStorage._data[key] || null,
|
|
71
|
+
setItem: (key, value) => { this._memoryStorage._data[key] = value },
|
|
72
|
+
removeItem: (key) => { delete this._memoryStorage._data[key] },
|
|
73
|
+
clear: () => { this._memoryStorage._data = {} }
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Set tokens and persist to storage
|
|
78
|
+
*/
|
|
79
|
+
setTokens (tokenData) {
|
|
80
|
+
const {
|
|
81
|
+
access_token: accessToken,
|
|
82
|
+
refresh_token: refreshToken,
|
|
83
|
+
expires_in: expiresIn,
|
|
84
|
+
token_type: tokenType = 'Bearer'
|
|
85
|
+
} = tokenData
|
|
86
|
+
|
|
87
|
+
if (!accessToken) {
|
|
88
|
+
throw new Error('Access token is required')
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Calculate expiry time
|
|
92
|
+
const now = Date.now()
|
|
93
|
+
const expiresAt = expiresIn ? now + (expiresIn * 1000) : null
|
|
94
|
+
|
|
95
|
+
// Update internal state
|
|
96
|
+
this.tokens = {
|
|
97
|
+
accessToken,
|
|
98
|
+
refreshToken: refreshToken || this.tokens.refreshToken,
|
|
99
|
+
expiresAt,
|
|
100
|
+
expiresIn,
|
|
101
|
+
tokenType
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Persist to storage
|
|
105
|
+
this.saveTokens()
|
|
106
|
+
|
|
107
|
+
// Schedule automatic refresh
|
|
108
|
+
this.scheduleRefresh()
|
|
109
|
+
|
|
110
|
+
// Trigger callback
|
|
111
|
+
if (this.config.onTokenRefresh) {
|
|
112
|
+
this.config.onTokenRefresh(this.tokens)
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return this.tokens
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Get current access token
|
|
120
|
+
*/
|
|
121
|
+
getAccessToken () {
|
|
122
|
+
return this.tokens.accessToken
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Get current refresh token
|
|
127
|
+
*/
|
|
128
|
+
getRefreshToken () {
|
|
129
|
+
return this.tokens.refreshToken
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Get authorization header value
|
|
134
|
+
*/
|
|
135
|
+
getAuthHeader () {
|
|
136
|
+
const token = this.getAccessToken()
|
|
137
|
+
if (!token) {return null}
|
|
138
|
+
|
|
139
|
+
return `${this.tokens.tokenType || 'Bearer'} ${token}`
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Check if access token is valid and not expired
|
|
144
|
+
*/
|
|
145
|
+
isAccessTokenValid () {
|
|
146
|
+
if (!this.tokens.accessToken) {return false}
|
|
147
|
+
if (!this.tokens.expiresAt) {return true} // No expiry info, assume valid
|
|
148
|
+
|
|
149
|
+
const now = Date.now()
|
|
150
|
+
return now < (this.tokens.expiresAt - this.config.refreshBuffer)
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Check if tokens exist (regardless of expiry)
|
|
155
|
+
*/
|
|
156
|
+
hasTokens () {
|
|
157
|
+
return Boolean(this.tokens.accessToken)
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Check if refresh token exists
|
|
162
|
+
*/
|
|
163
|
+
hasRefreshToken () {
|
|
164
|
+
return Boolean(this.tokens.refreshToken)
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Automatically refresh tokens if needed
|
|
169
|
+
*/
|
|
170
|
+
async ensureValidToken () {
|
|
171
|
+
// If no tokens, return null
|
|
172
|
+
if (!this.hasTokens()) {
|
|
173
|
+
return null
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// If token is still valid, return it
|
|
177
|
+
if (this.isAccessTokenValid()) {
|
|
178
|
+
return this.getAccessToken()
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// If no refresh token, clear tokens and return null
|
|
182
|
+
if (!this.hasRefreshToken()) {
|
|
183
|
+
this.clearTokens()
|
|
184
|
+
if (this.config.onTokenExpired) {
|
|
185
|
+
this.config.onTokenExpired()
|
|
186
|
+
}
|
|
187
|
+
return null
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Attempt to refresh token
|
|
191
|
+
try {
|
|
192
|
+
await this.refreshTokens()
|
|
193
|
+
return this.getAccessToken()
|
|
194
|
+
} catch (error) {
|
|
195
|
+
this.clearTokens()
|
|
196
|
+
if (this.config.onTokenError) {
|
|
197
|
+
this.config.onTokenError(error)
|
|
198
|
+
}
|
|
199
|
+
throw error
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Refresh access token using refresh token
|
|
205
|
+
*/
|
|
206
|
+
async refreshTokens () {
|
|
207
|
+
// Prevent multiple simultaneous refresh requests
|
|
208
|
+
if (this.refreshPromise) {
|
|
209
|
+
return this.refreshPromise
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
if (!this.hasRefreshToken()) {
|
|
213
|
+
throw new Error('No refresh token available')
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
if (this.retryCount >= this.config.maxRetries) {
|
|
217
|
+
throw new Error('Max refresh retries exceeded')
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
this.refreshPromise = this._performRefresh()
|
|
221
|
+
|
|
222
|
+
try {
|
|
223
|
+
const result = await this.refreshPromise
|
|
224
|
+
this.retryCount = 0 // Reset retry count on success
|
|
225
|
+
return result
|
|
226
|
+
} catch (error) {
|
|
227
|
+
this.retryCount++
|
|
228
|
+
throw error
|
|
229
|
+
} finally {
|
|
230
|
+
this.refreshPromise = null
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Perform the actual token refresh request
|
|
236
|
+
*/
|
|
237
|
+
async _performRefresh () {
|
|
238
|
+
const refreshToken = this.getRefreshToken()
|
|
239
|
+
|
|
240
|
+
const response = await fetch(`${this.config.apiUrl}/core/auth/refresh`, {
|
|
241
|
+
method: 'POST',
|
|
242
|
+
headers: {
|
|
243
|
+
'Content-Type': 'application/json'
|
|
244
|
+
},
|
|
245
|
+
body: JSON.stringify({ refreshToken })
|
|
246
|
+
})
|
|
247
|
+
|
|
248
|
+
if (!response.ok) {
|
|
249
|
+
const errorData = await response.json().catch(() => ({}))
|
|
250
|
+
throw new Error(errorData.message || `Token refresh failed: ${response.status}`)
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
const responseData = await response.json()
|
|
254
|
+
|
|
255
|
+
// Handle new response format: responseData.data.tokens
|
|
256
|
+
if (responseData.success && responseData.data && responseData.data.tokens) {
|
|
257
|
+
const { tokens } = responseData.data
|
|
258
|
+
const tokenData = {
|
|
259
|
+
access_token: tokens.accessToken,
|
|
260
|
+
refresh_token: tokens.refreshToken,
|
|
261
|
+
expires_in: tokens.accessTokenExp?.expiresIn,
|
|
262
|
+
token_type: 'Bearer'
|
|
263
|
+
}
|
|
264
|
+
return this.setTokens(tokenData)
|
|
265
|
+
}
|
|
266
|
+
// Fallback to old format for backward compatibility
|
|
267
|
+
return this.setTokens(responseData)
|
|
268
|
+
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Schedule automatic token refresh
|
|
273
|
+
*/
|
|
274
|
+
scheduleRefresh () {
|
|
275
|
+
// Clear existing timeout
|
|
276
|
+
if (this.refreshTimeout) {
|
|
277
|
+
clearTimeout(this.refreshTimeout)
|
|
278
|
+
this.refreshTimeout = null
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// Don't schedule if no expiry info or no refresh token
|
|
282
|
+
if (!this.tokens.expiresAt || !this.hasRefreshToken()) {
|
|
283
|
+
return
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
const now = Date.now()
|
|
287
|
+
const refreshTime = this.tokens.expiresAt - this.config.refreshBuffer
|
|
288
|
+
const delay = Math.max(0, refreshTime - now)
|
|
289
|
+
|
|
290
|
+
this.refreshTimeout = setTimeout(async () => {
|
|
291
|
+
try {
|
|
292
|
+
await this.refreshTokens()
|
|
293
|
+
} catch (error) {
|
|
294
|
+
console.error('Automatic token refresh failed:', error)
|
|
295
|
+
if (this.config.onTokenError) {
|
|
296
|
+
this.config.onTokenError(error)
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
}, delay)
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* Save tokens to storage
|
|
304
|
+
*/
|
|
305
|
+
saveTokens () {
|
|
306
|
+
const {storage} = this
|
|
307
|
+
const keys = this.storageKeys
|
|
308
|
+
|
|
309
|
+
if (this.tokens.accessToken) {
|
|
310
|
+
storage.setItem(keys.accessToken, this.tokens.accessToken)
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
if (this.tokens.refreshToken) {
|
|
314
|
+
storage.setItem(keys.refreshToken, this.tokens.refreshToken)
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
if (this.tokens.expiresAt) {
|
|
318
|
+
storage.setItem(keys.expiresAt, this.tokens.expiresAt.toString())
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
if (this.tokens.expiresIn) {
|
|
322
|
+
storage.setItem(keys.expiresIn, this.tokens.expiresIn.toString())
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
/**
|
|
327
|
+
* Load tokens from storage
|
|
328
|
+
*/
|
|
329
|
+
loadTokens () {
|
|
330
|
+
const {storage} = this
|
|
331
|
+
const keys = this.storageKeys
|
|
332
|
+
|
|
333
|
+
const accessToken = storage.getItem(keys.accessToken)
|
|
334
|
+
const refreshToken = storage.getItem(keys.refreshToken)
|
|
335
|
+
const expiresAt = storage.getItem(keys.expiresAt)
|
|
336
|
+
const expiresIn = storage.getItem(keys.expiresIn)
|
|
337
|
+
|
|
338
|
+
if (accessToken) {
|
|
339
|
+
this.tokens = {
|
|
340
|
+
accessToken,
|
|
341
|
+
refreshToken,
|
|
342
|
+
expiresAt: expiresAt ? parseInt(expiresAt, 10) : null,
|
|
343
|
+
expiresIn: expiresIn ? parseInt(expiresIn, 10) : null,
|
|
344
|
+
tokenType: 'Bearer'
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
// Schedule refresh for loaded tokens
|
|
348
|
+
this.scheduleRefresh()
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
* Clear all tokens
|
|
354
|
+
*/
|
|
355
|
+
clearTokens () {
|
|
356
|
+
// Clear memory
|
|
357
|
+
this.tokens = {
|
|
358
|
+
accessToken: null,
|
|
359
|
+
refreshToken: null,
|
|
360
|
+
expiresAt: null,
|
|
361
|
+
expiresIn: null
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
// Clear storage
|
|
365
|
+
const {storage} = this
|
|
366
|
+
const keys = this.storageKeys
|
|
367
|
+
|
|
368
|
+
Object.values(keys).forEach(key => {
|
|
369
|
+
storage.removeItem(key)
|
|
370
|
+
})
|
|
371
|
+
|
|
372
|
+
// Clear scheduled refresh
|
|
373
|
+
if (this.refreshTimeout) {
|
|
374
|
+
clearTimeout(this.refreshTimeout)
|
|
375
|
+
this.refreshTimeout = null
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
// Reset retry count
|
|
379
|
+
this.retryCount = 0
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* Get token status information
|
|
384
|
+
*/
|
|
385
|
+
getTokenStatus () {
|
|
386
|
+
const hasTokens = this.hasTokens()
|
|
387
|
+
const isValid = this.isAccessTokenValid()
|
|
388
|
+
const {expiresAt} = this.tokens
|
|
389
|
+
const timeToExpiry = expiresAt ? expiresAt - Date.now() : null
|
|
390
|
+
|
|
391
|
+
return {
|
|
392
|
+
hasTokens,
|
|
393
|
+
isValid,
|
|
394
|
+
hasRefreshToken: this.hasRefreshToken(),
|
|
395
|
+
expiresAt,
|
|
396
|
+
timeToExpiry,
|
|
397
|
+
willExpireSoon: timeToExpiry ? timeToExpiry < this.config.refreshBuffer : false
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
/**
|
|
402
|
+
* Cleanup resources
|
|
403
|
+
*/
|
|
404
|
+
destroy () {
|
|
405
|
+
if (this.refreshTimeout) {
|
|
406
|
+
clearTimeout(this.refreshTimeout)
|
|
407
|
+
this.refreshTimeout = null
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
this.refreshPromise = null
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
// Export singleton instance
|
|
415
|
+
let defaultTokenManager = null
|
|
416
|
+
|
|
417
|
+
export const getTokenManager = (options) => {
|
|
418
|
+
if (!defaultTokenManager) {
|
|
419
|
+
defaultTokenManager = new TokenManager(options)
|
|
420
|
+
}
|
|
421
|
+
return defaultTokenManager
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
export const createTokenManager = (options) => new TokenManager(options)
|
package/src/utils/basedQuerys.js
CHANGED
|
@@ -39,3 +39,126 @@ export const buildUserQuery = userId => ({
|
|
|
39
39
|
$all: true
|
|
40
40
|
}
|
|
41
41
|
})
|
|
42
|
+
|
|
43
|
+
export const buildGetUserDataQuery = userId => ({
|
|
44
|
+
$id: userId,
|
|
45
|
+
id: true,
|
|
46
|
+
name: true,
|
|
47
|
+
email: true,
|
|
48
|
+
username: true,
|
|
49
|
+
globalRole: true,
|
|
50
|
+
updatedAt: true,
|
|
51
|
+
createdAt: true,
|
|
52
|
+
memberProjects: {
|
|
53
|
+
$list: true,
|
|
54
|
+
id: true,
|
|
55
|
+
role: true,
|
|
56
|
+
createdAt: true,
|
|
57
|
+
updatedAt: true,
|
|
58
|
+
project: {
|
|
59
|
+
id: true,
|
|
60
|
+
key: true,
|
|
61
|
+
name: true,
|
|
62
|
+
thumbnail: true,
|
|
63
|
+
icon: true,
|
|
64
|
+
tier: true,
|
|
65
|
+
visibility: true,
|
|
66
|
+
access: true,
|
|
67
|
+
members: {
|
|
68
|
+
$list: true,
|
|
69
|
+
user: {
|
|
70
|
+
id: true,
|
|
71
|
+
name: true,
|
|
72
|
+
email: true,
|
|
73
|
+
globalRole: true
|
|
74
|
+
},
|
|
75
|
+
role: true,
|
|
76
|
+
updatedAt: true,
|
|
77
|
+
createdAt: true
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
export const buildGetProjectsByKeysQuery = keys => (
|
|
84
|
+
{
|
|
85
|
+
projects: {
|
|
86
|
+
id: true,
|
|
87
|
+
key: true,
|
|
88
|
+
name: true,
|
|
89
|
+
thumbnail: true,
|
|
90
|
+
icon: true,
|
|
91
|
+
tier: true,
|
|
92
|
+
visibility: true,
|
|
93
|
+
access: true,
|
|
94
|
+
members: {
|
|
95
|
+
$list: true,
|
|
96
|
+
user: {
|
|
97
|
+
id: true,
|
|
98
|
+
name: true,
|
|
99
|
+
email: true,
|
|
100
|
+
globalRole: true
|
|
101
|
+
},
|
|
102
|
+
role: true,
|
|
103
|
+
updatedAt: true,
|
|
104
|
+
createdAt: true
|
|
105
|
+
},
|
|
106
|
+
$list: {
|
|
107
|
+
$find: {
|
|
108
|
+
$traverse: "children",
|
|
109
|
+
$filter: [
|
|
110
|
+
{ $field: "type", $operator: "=", $value: "project" },
|
|
111
|
+
{ $field: "key", $operator: "=", $value: keys }
|
|
112
|
+
]
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
const GetProjectFields = {
|
|
120
|
+
id: true,
|
|
121
|
+
name: true,
|
|
122
|
+
key: true,
|
|
123
|
+
tier: true,
|
|
124
|
+
projectType: true,
|
|
125
|
+
icon: true,
|
|
126
|
+
package: true,
|
|
127
|
+
seats: true,
|
|
128
|
+
projectPassword: true,
|
|
129
|
+
stripe: true,
|
|
130
|
+
payments:{
|
|
131
|
+
$list: true,
|
|
132
|
+
id: true,
|
|
133
|
+
name: true
|
|
134
|
+
},
|
|
135
|
+
access: true,
|
|
136
|
+
isSharedLibrary: true,
|
|
137
|
+
framework: true,
|
|
138
|
+
designTool: true,
|
|
139
|
+
language: true,
|
|
140
|
+
visibility: true,
|
|
141
|
+
domains: true,
|
|
142
|
+
subscription:{id: true},
|
|
143
|
+
members: {
|
|
144
|
+
$list: true,
|
|
145
|
+
user: {id: true, name: true, email: true},
|
|
146
|
+
role: true,
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export const buildGetProjectDataQuery = projectId => ({
|
|
151
|
+
$id: projectId,
|
|
152
|
+
...GetProjectFields
|
|
153
|
+
})
|
|
154
|
+
|
|
155
|
+
export const buildGetProjectByKeyDataQuery = key => ({
|
|
156
|
+
...GetProjectFields,
|
|
157
|
+
$find: {
|
|
158
|
+
$traverse: 'children',
|
|
159
|
+
$filter: [
|
|
160
|
+
{ $field: 'type', $operator: '=', $value: 'project' },
|
|
161
|
+
{ $field: 'key', $operator: '=', $value: key }
|
|
162
|
+
]
|
|
163
|
+
}
|
|
164
|
+
})
|
package/src/utils/permission.js
CHANGED
|
@@ -84,12 +84,11 @@ export const PERMISSION_MAP = {
|
|
|
84
84
|
|
|
85
85
|
export const ROLE_PERMISSIONS = {
|
|
86
86
|
guest: ['viewPublicProjects'],
|
|
87
|
-
user: ['viewPublicProjects'
|
|
88
|
-
admin: ['viewPublicProjects', '
|
|
87
|
+
user: ['viewPublicProjects'],
|
|
88
|
+
admin: ['viewPublicProjects', 'governance'],
|
|
89
89
|
superAdmin: [
|
|
90
90
|
'viewPublicProjects',
|
|
91
|
-
'
|
|
92
|
-
'manageUsers',
|
|
91
|
+
'governance',
|
|
93
92
|
'managePlatform'
|
|
94
93
|
]
|
|
95
94
|
}
|
|
@@ -187,6 +186,7 @@ export const TIER_FEATURES = {
|
|
|
187
186
|
}
|
|
188
187
|
|
|
189
188
|
export const PROJECT_ROLE_PERMISSIONS = {
|
|
189
|
+
unauthenticated: ['platformSettings', 'showContent'],
|
|
190
190
|
guest: ['platformSettings', 'showContent'],
|
|
191
191
|
editor: [
|
|
192
192
|
'platformSettings',
|
package/src/utils/services.js
CHANGED
|
@@ -1,24 +1,24 @@
|
|
|
1
1
|
export const SERVICE_METHODS = {
|
|
2
|
-
// Auth service methods
|
|
2
|
+
// Auth service methods (legacy - keeping for backward compatibility)
|
|
3
3
|
auth: 'auth',
|
|
4
4
|
login: 'auth',
|
|
5
5
|
register: 'auth',
|
|
6
6
|
googleAuth: 'auth',
|
|
7
|
+
googleAuthCallback: 'auth',
|
|
7
8
|
githubAuth: 'auth',
|
|
8
9
|
confirmRegistration: 'auth',
|
|
9
10
|
logout: 'auth',
|
|
10
11
|
updateUserRole: 'auth',
|
|
11
12
|
hasPermission: 'auth',
|
|
13
|
+
hasGlobalPermission: 'auth',
|
|
12
14
|
getProjectMembers: 'auth',
|
|
13
15
|
inviteMember: 'auth',
|
|
14
16
|
acceptInvite: 'auth',
|
|
15
17
|
updateMemberRole: 'auth',
|
|
16
18
|
removeMember: 'auth',
|
|
17
19
|
updateProjectTier: 'auth',
|
|
18
|
-
requestPasswordReset: 'auth',
|
|
19
|
-
confirmPasswordReset: 'auth',
|
|
20
20
|
subscribeToAuthChanges: 'auth',
|
|
21
|
-
getStoredAuthState: '
|
|
21
|
+
getStoredAuthState: 'core',
|
|
22
22
|
|
|
23
23
|
// AI service methods
|
|
24
24
|
prompt: 'ai',
|
|
@@ -27,6 +27,7 @@ export const SERVICE_METHODS = {
|
|
|
27
27
|
query: 'based',
|
|
28
28
|
setProject: 'based',
|
|
29
29
|
setUser: 'based',
|
|
30
|
+
setUserForced: 'based',
|
|
30
31
|
subscribe: 'based',
|
|
31
32
|
call: 'based',
|
|
32
33
|
getProject: 'based',
|
|
@@ -62,30 +63,52 @@ export const SERVICE_METHODS = {
|
|
|
62
63
|
confirmPasswordChange: 'based',
|
|
63
64
|
updateUserProfile: 'based',
|
|
64
65
|
duplicateProject: 'based',
|
|
66
|
+
listPlans: 'based',
|
|
67
|
+
subscribeToPlan: 'based',
|
|
68
|
+
getSubscriptionDetails: 'based',
|
|
69
|
+
checkSubscriptionStatus: 'based',
|
|
70
|
+
upgradeSubscription: 'based',
|
|
71
|
+
downgradeSubscription: 'based',
|
|
72
|
+
cancelSubscription: 'based',
|
|
73
|
+
reactivateSubscription: 'based',
|
|
74
|
+
generateInvoice: 'based',
|
|
75
|
+
getUsageReport: 'based',
|
|
76
|
+
inviteAccountOwner: 'based',
|
|
77
|
+
acceptOwnerInvitation: 'based',
|
|
78
|
+
removeAccountOwner: 'based',
|
|
79
|
+
checkResourceLimit: 'based',
|
|
80
|
+
checkFeatureAccess: 'based',
|
|
81
|
+
getUserFeatures: 'based',
|
|
82
|
+
getAvailableFeatures: 'based',
|
|
83
|
+
listFeatureFlags: 'based',
|
|
84
|
+
updateFeatureFlag: 'based',
|
|
85
|
+
removeFeatureFlag: 'based',
|
|
86
|
+
batchUpdateFeatureFlags: 'based',
|
|
87
|
+
updatePlanDetails: 'based',
|
|
88
|
+
updatePlanStatus: 'based',
|
|
65
89
|
|
|
66
90
|
// Symstory service methods
|
|
67
91
|
set: 'symstory',
|
|
68
92
|
getData: 'symstory',
|
|
69
93
|
updateData: 'symstory',
|
|
70
94
|
getBranches: 'symstory',
|
|
71
|
-
createBranch: 'symstory',
|
|
72
95
|
editBranch: 'symstory',
|
|
73
|
-
deleteBranch: 'symstory',
|
|
74
|
-
mergeBranch: 'symstory',
|
|
75
96
|
restoreVersion: 'symstory',
|
|
76
97
|
getItem: 'symstory',
|
|
77
98
|
addItem: 'symstory',
|
|
99
|
+
addMultipleItems: 'symstory',
|
|
78
100
|
updateItem: 'symstory',
|
|
79
101
|
deleteItem: 'symstory',
|
|
80
102
|
switchVersion: 'symstory',
|
|
81
103
|
switchBranch: 'symstory',
|
|
82
104
|
undo: 'symstory',
|
|
83
105
|
redo: 'symstory',
|
|
106
|
+
publish: 'symstory',
|
|
107
|
+
getChanges: 'symstory',
|
|
84
108
|
|
|
85
109
|
// Socket service methods
|
|
86
110
|
send: 'socket',
|
|
87
111
|
subscribeChannel: 'socket',
|
|
88
112
|
connect: 'socket',
|
|
89
|
-
reconnect: 'socket'
|
|
90
|
-
destroy: 'socket'
|
|
113
|
+
reconnect: 'socket'
|
|
91
114
|
}
|
|
@@ -30,7 +30,7 @@ class SymstoryClient {
|
|
|
30
30
|
* @returns {Promise<any>} - The response data.
|
|
31
31
|
*/
|
|
32
32
|
async request (path = '', options = {}) {
|
|
33
|
-
const url = `${this.options.baseUrl}/${this.appKey}${path}`
|
|
33
|
+
const url = `${this.options.baseUrl}/symstory/${this.appKey}${path}`
|
|
34
34
|
const response = await fetch(url, {
|
|
35
35
|
...options,
|
|
36
36
|
headers: { ...this.headers, ...options.headers }
|
|
@@ -194,6 +194,40 @@ class SymstoryClient {
|
|
|
194
194
|
body: JSON.stringify({ branch, version, type, message })
|
|
195
195
|
})
|
|
196
196
|
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Publishes an existing version of the project.
|
|
200
|
+
* @param {string} version - The version ID/number to publish.
|
|
201
|
+
* @param {object} [options={}] - The publishing options.
|
|
202
|
+
* @param {string} [options.branch='main'] - The branch name. (Only if version number is provided)
|
|
203
|
+
* @returns {Promise<any>} - The response data.
|
|
204
|
+
*/
|
|
205
|
+
publishVersion (version, { branch = 'main' } = {}) {
|
|
206
|
+
return this.request('/publish', {
|
|
207
|
+
method: 'POST',
|
|
208
|
+
body: JSON.stringify({ version })
|
|
209
|
+
})
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Retrieves all changes after a specific version.
|
|
214
|
+
* @param {object} [options={}] - The changes options.
|
|
215
|
+
* @param {string} [options.versionId] - The version ID to publish.
|
|
216
|
+
* @param {string} [options.versionValue] - The version ID to publish. (alternative to versionId)
|
|
217
|
+
* @param {string} [options.branch] - The branch to publish (Only in combination to versionValue)
|
|
218
|
+
* @returns {Promise<any>} - The changes data.
|
|
219
|
+
*/
|
|
220
|
+
getChanges ({ versionId, versionValue, branch } = {}) {
|
|
221
|
+
return this.request(
|
|
222
|
+
'/changes?' +
|
|
223
|
+
new URLSearchParams({
|
|
224
|
+
...(versionId ? { versionId } : {}),
|
|
225
|
+
...(versionValue ? { versionValue } : {}),
|
|
226
|
+
...(branch ? { branch } : {})
|
|
227
|
+
}).toString(),
|
|
228
|
+
{}
|
|
229
|
+
)
|
|
230
|
+
}
|
|
197
231
|
}
|
|
198
232
|
|
|
199
233
|
export default {
|