@symbo.ls/sdk 3.2.3 → 3.2.7
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 +141 -0
- package/dist/cjs/config/environment.js +94 -10
- package/dist/cjs/index.js +152 -12
- package/dist/cjs/services/AdminService.js +351 -0
- package/dist/cjs/services/AuthService.js +738 -305
- package/dist/cjs/services/BaseService.js +158 -6
- package/dist/cjs/services/BranchService.js +484 -0
- package/dist/cjs/services/CollabService.js +439 -116
- package/dist/cjs/services/DnsService.js +340 -0
- package/dist/cjs/services/FeatureFlagService.js +175 -0
- package/dist/cjs/services/FileService.js +201 -0
- package/dist/cjs/services/IntegrationService.js +538 -0
- package/dist/cjs/services/MetricsService.js +62 -0
- package/dist/cjs/services/PaymentService.js +271 -0
- package/dist/cjs/services/PlanService.js +426 -0
- package/dist/cjs/services/ProjectService.js +1207 -0
- package/dist/cjs/services/PullRequestService.js +503 -0
- package/dist/cjs/services/ScreenshotService.js +304 -0
- package/dist/cjs/services/SubscriptionService.js +396 -0
- package/dist/cjs/services/TrackingService.js +661 -0
- package/dist/cjs/services/WaitlistService.js +148 -0
- package/dist/cjs/services/index.js +60 -4
- package/dist/cjs/state/RootStateManager.js +2 -23
- package/dist/cjs/state/rootEventBus.js +9 -0
- package/dist/cjs/utils/CollabClient.js +78 -12
- package/dist/cjs/utils/TokenManager.js +16 -3
- package/dist/cjs/utils/changePreprocessor.js +199 -0
- package/dist/cjs/utils/jsonDiff.js +46 -4
- package/dist/cjs/utils/ordering.js +309 -0
- package/dist/cjs/utils/services.js +285 -128
- package/dist/cjs/utils/validation.js +0 -3
- package/dist/esm/config/environment.js +94 -10
- package/dist/esm/index.js +47862 -18248
- package/dist/esm/services/AdminService.js +1132 -0
- package/dist/esm/services/AuthService.js +1493 -386
- package/dist/esm/services/BaseService.js +757 -6
- package/dist/esm/services/BranchService.js +1265 -0
- package/dist/esm/services/CollabService.js +24956 -16089
- package/dist/esm/services/DnsService.js +1121 -0
- package/dist/esm/services/FeatureFlagService.js +956 -0
- package/dist/esm/services/FileService.js +982 -0
- package/dist/esm/services/IntegrationService.js +1319 -0
- package/dist/esm/services/MetricsService.js +843 -0
- package/dist/esm/services/PaymentService.js +1052 -0
- package/dist/esm/services/PlanService.js +1207 -0
- package/dist/esm/services/ProjectService.js +2526 -0
- package/dist/esm/services/PullRequestService.js +1284 -0
- package/dist/esm/services/ScreenshotService.js +1085 -0
- package/dist/esm/services/SubscriptionService.js +1177 -0
- package/dist/esm/services/TrackingService.js +18454 -0
- package/dist/esm/services/WaitlistService.js +929 -0
- package/dist/esm/services/index.js +47373 -18027
- package/dist/esm/state/RootStateManager.js +11 -23
- package/dist/esm/state/rootEventBus.js +9 -0
- package/dist/esm/utils/CollabClient.js +17526 -16120
- package/dist/esm/utils/TokenManager.js +16 -3
- package/dist/esm/utils/changePreprocessor.js +542 -0
- package/dist/esm/utils/jsonDiff.js +958 -43
- package/dist/esm/utils/ordering.js +291 -0
- package/dist/esm/utils/services.js +285 -128
- package/dist/esm/utils/validation.js +116 -50
- package/dist/node/config/environment.js +94 -10
- package/dist/node/index.js +183 -16
- package/dist/node/services/AdminService.js +332 -0
- package/dist/node/services/AuthService.js +742 -310
- package/dist/node/services/BaseService.js +148 -6
- package/dist/node/services/BranchService.js +465 -0
- package/dist/node/services/CollabService.js +439 -116
- package/dist/node/services/DnsService.js +321 -0
- package/dist/node/services/FeatureFlagService.js +156 -0
- package/dist/node/services/FileService.js +182 -0
- package/dist/node/services/IntegrationService.js +519 -0
- package/dist/node/services/MetricsService.js +43 -0
- package/dist/node/services/PaymentService.js +252 -0
- package/dist/node/services/PlanService.js +407 -0
- package/dist/node/services/ProjectService.js +1188 -0
- package/dist/node/services/PullRequestService.js +484 -0
- package/dist/node/services/ScreenshotService.js +285 -0
- package/dist/node/services/SubscriptionService.js +377 -0
- package/dist/node/services/TrackingService.js +632 -0
- package/dist/node/services/WaitlistService.js +129 -0
- package/dist/node/services/index.js +60 -4
- package/dist/node/state/RootStateManager.js +2 -23
- package/dist/node/state/rootEventBus.js +9 -0
- package/dist/node/utils/CollabClient.js +77 -11
- package/dist/node/utils/TokenManager.js +16 -3
- package/dist/node/utils/changePreprocessor.js +180 -0
- package/dist/node/utils/jsonDiff.js +46 -4
- package/dist/node/utils/ordering.js +290 -0
- package/dist/node/utils/services.js +285 -128
- package/dist/node/utils/validation.js +0 -3
- package/package.json +30 -18
- package/src/config/environment.js +95 -10
- package/src/index.js +190 -23
- package/src/services/AdminService.js +374 -0
- package/src/services/AuthService.js +874 -328
- package/src/services/BaseService.js +166 -6
- package/src/services/BranchService.js +536 -0
- package/src/services/CollabService.js +557 -148
- package/src/services/DnsService.js +366 -0
- package/src/services/FeatureFlagService.js +174 -0
- package/src/services/FileService.js +213 -0
- package/src/services/IntegrationService.js +548 -0
- package/src/services/MetricsService.js +40 -0
- package/src/services/PaymentService.js +287 -0
- package/src/services/PlanService.js +468 -0
- package/src/services/ProjectService.js +1366 -0
- package/src/services/PullRequestService.js +537 -0
- package/src/services/ScreenshotService.js +258 -0
- package/src/services/SubscriptionService.js +425 -0
- package/src/services/TrackingService.js +853 -0
- package/src/services/WaitlistService.js +130 -0
- package/src/services/index.js +79 -5
- package/src/services/tests/BranchService/createBranch.test.js +153 -0
- package/src/services/tests/BranchService/deleteBranch.test.js +173 -0
- package/src/services/tests/BranchService/getBranchChanges.test.js +146 -0
- package/src/services/tests/BranchService/listBranches.test.js +87 -0
- package/src/services/tests/BranchService/mergeBranch.test.js +210 -0
- package/src/services/tests/BranchService/publishVersion.test.js +183 -0
- package/src/services/tests/BranchService/renameBranch.test.js +240 -0
- package/src/services/tests/BranchService/resetBranch.test.js +152 -0
- package/src/services/tests/FeatureFlagService/adminFeatureFlags.test.js +67 -0
- package/src/services/tests/FeatureFlagService/getFeatureFlags.test.js +75 -0
- package/src/services/tests/FileService/createFileFormData.test.js +74 -0
- package/src/services/tests/FileService/getFileUrl.test.js +69 -0
- package/src/services/tests/FileService/updateProjectIcon.test.js +109 -0
- package/src/services/tests/FileService/uploadDocument.test.js +36 -0
- package/src/services/tests/FileService/uploadFile.test.js +78 -0
- package/src/services/tests/FileService/uploadFileWithValidation.test.js +114 -0
- package/src/services/tests/FileService/uploadImage.test.js +36 -0
- package/src/services/tests/FileService/uploadMultipleFiles.test.js +111 -0
- package/src/services/tests/FileService/validateFile.test.js +63 -0
- package/src/services/tests/PlanService/createPlan.test.js +104 -0
- package/src/services/tests/PlanService/createPlanWithValidation.test.js +523 -0
- package/src/services/tests/PlanService/deletePlan.test.js +92 -0
- package/src/services/tests/PlanService/getActivePlans.test.js +123 -0
- package/src/services/tests/PlanService/getAdminPlans.test.js +84 -0
- package/src/services/tests/PlanService/getPlan.test.js +50 -0
- package/src/services/tests/PlanService/getPlanByKey.test.js +109 -0
- package/src/services/tests/PlanService/getPlanWithValidation.test.js +85 -0
- package/src/services/tests/PlanService/getPlans.test.js +53 -0
- package/src/services/tests/PlanService/getPlansByPriceRange.test.js +109 -0
- package/src/services/tests/PlanService/getPlansWithValidation.test.js +48 -0
- package/src/services/tests/PlanService/initializePlans.test.js +75 -0
- package/src/services/tests/PlanService/updatePlan.test.js +111 -0
- package/src/services/tests/PlanService/updatePlanWithValidation.test.js +556 -0
- package/src/state/RootStateManager.js +37 -32
- package/src/state/rootEventBus.js +19 -0
- package/src/utils/CollabClient.js +99 -12
- package/src/utils/TokenManager.js +20 -3
- package/src/utils/changePreprocessor.js +239 -0
- package/src/utils/jsonDiff.js +40 -5
- package/src/utils/ordering.js +271 -0
- package/src/utils/services.js +306 -139
- package/src/utils/validation.js +0 -3
- package/dist/cjs/services/AIService.js +0 -155
- package/dist/cjs/services/BasedService.js +0 -1185
- package/dist/cjs/services/CoreService.js +0 -2295
- package/dist/cjs/services/SocketService.js +0 -309
- package/dist/cjs/services/SymstoryService.js +0 -571
- package/dist/cjs/utils/basedQuerys.js +0 -181
- package/dist/cjs/utils/symstoryClient.js +0 -259
- package/dist/esm/services/AIService.js +0 -185
- package/dist/esm/services/BasedService.js +0 -5262
- package/dist/esm/services/CoreService.js +0 -2827
- package/dist/esm/services/SocketService.js +0 -456
- package/dist/esm/services/SymstoryService.js +0 -7025
- package/dist/esm/utils/basedQuerys.js +0 -163
- package/dist/esm/utils/symstoryClient.js +0 -354
- package/dist/node/services/AIService.js +0 -136
- package/dist/node/services/BasedService.js +0 -1156
- package/dist/node/services/CoreService.js +0 -2266
- package/dist/node/services/SocketService.js +0 -280
- package/dist/node/services/SymstoryService.js +0 -542
- package/dist/node/utils/basedQuerys.js +0 -162
- package/dist/node/utils/symstoryClient.js +0 -230
- package/src/services/AIService.js +0 -150
- package/src/services/BasedService.js +0 -1302
- package/src/services/CoreService.js +0 -2548
- package/src/services/SocketService.js +0 -336
- package/src/services/SymstoryService.js +0 -649
- package/src/utils/basedQuerys.js +0 -164
- package/src/utils/symstoryClient.js +0 -252
|
@@ -1,3 +1,613 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
3
|
+
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
4
|
+
|
|
5
|
+
// src/config/environment.js
|
|
6
|
+
import { isDevelopment } from "@domql/utils";
|
|
7
|
+
var CONFIG = {
|
|
8
|
+
// Common defaults for all environments
|
|
9
|
+
common: {
|
|
10
|
+
// NOTE: Google client id for google auth, need to configure URLs for each environment in Google console
|
|
11
|
+
googleClientId: "686286207466-bvd2fqs31rlm64fgich7rtpnc8ns2tqg.apps.googleusercontent.com",
|
|
12
|
+
// Feature toggles that apply across all environments by default
|
|
13
|
+
features: {
|
|
14
|
+
newUserOnboarding: true,
|
|
15
|
+
betaFeatures: false,
|
|
16
|
+
// Tracking is enabled by default unless overridden per environment
|
|
17
|
+
trackingEnabled: true
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
// Environment-specific configurations
|
|
21
|
+
local: {
|
|
22
|
+
// local
|
|
23
|
+
socketUrl: "http://localhost:8080",
|
|
24
|
+
// For socket api
|
|
25
|
+
apiUrl: "http://localhost:8080",
|
|
26
|
+
// For server api
|
|
27
|
+
basedEnv: "development",
|
|
28
|
+
// For based api
|
|
29
|
+
basedProject: "platform-v2-sm",
|
|
30
|
+
// For based api
|
|
31
|
+
basedOrg: "symbols",
|
|
32
|
+
// For based api
|
|
33
|
+
githubClientId: "Ov23liAFrsR0StbAO6PO",
|
|
34
|
+
// For github api
|
|
35
|
+
grafanaUrl: "",
|
|
36
|
+
// For grafana tracing
|
|
37
|
+
grafanaAppName: "Symbols Localhost",
|
|
38
|
+
// Environment-specific feature toggles (override common)
|
|
39
|
+
features: {
|
|
40
|
+
// Disable tracking by default on localhost/dev machines
|
|
41
|
+
trackingEnabled: false,
|
|
42
|
+
// Enable beta features in local dev
|
|
43
|
+
betaFeatures: true,
|
|
44
|
+
// Preserve common defaults explicitly for local
|
|
45
|
+
newUserOnboarding: true
|
|
46
|
+
},
|
|
47
|
+
typesenseCollectionName: "docs",
|
|
48
|
+
typesenseApiKey: "vZya3L2zpq8L6iI5WWMUZJZABvT63VDb",
|
|
49
|
+
typesenseHost: "localhost",
|
|
50
|
+
typesensePort: "8108",
|
|
51
|
+
typesenseProtocol: "http"
|
|
52
|
+
},
|
|
53
|
+
development: {
|
|
54
|
+
socketUrl: "https://dev.api.symbols.app",
|
|
55
|
+
apiUrl: "https://dev.api.symbols.app",
|
|
56
|
+
githubClientId: "Ov23liHxyWFBxS8f1gnF",
|
|
57
|
+
grafanaUrl: "https://faro-collector-prod-us-east-0.grafana.net/collect/7a3ba473cee2025c68513667024316b8",
|
|
58
|
+
// For grafana tracing
|
|
59
|
+
grafanaAppName: "Symbols Dev",
|
|
60
|
+
typesenseCollectionName: "docs",
|
|
61
|
+
typesenseApiKey: "awmcVpbWqZi9IUgmvslp1C5LKDU8tMjA",
|
|
62
|
+
typesenseHost: "tl2qpnwxev4cjm36p-1.a1.typesense.net",
|
|
63
|
+
typesensePort: "443",
|
|
64
|
+
typesenseProtocol: "https"
|
|
65
|
+
},
|
|
66
|
+
testing: {
|
|
67
|
+
socketUrl: "https://test.api.symbols.app",
|
|
68
|
+
apiUrl: "https://test.api.symbols.app",
|
|
69
|
+
basedEnv: "testing",
|
|
70
|
+
basedProject: "platform-v2-sm",
|
|
71
|
+
basedOrg: "symbols",
|
|
72
|
+
githubClientId: "Ov23liHxyWFBxS8f1gnF",
|
|
73
|
+
grafanaUrl: "",
|
|
74
|
+
// For grafana tracing
|
|
75
|
+
grafanaAppName: "Symbols Test",
|
|
76
|
+
typesenseCollectionName: "docs",
|
|
77
|
+
typesenseApiKey: "awmcVpbWqZi9IUgmvslp1C5LKDU8tMjA",
|
|
78
|
+
typesenseHost: "tl2qpnwxev4cjm36p-1.a1.typesense.net",
|
|
79
|
+
typesensePort: "443",
|
|
80
|
+
typesenseProtocol: "https"
|
|
81
|
+
},
|
|
82
|
+
upcoming: {
|
|
83
|
+
socketUrl: "https://upcoming.api.symbols.app",
|
|
84
|
+
apiUrl: "https://upcoming.api.symbols.app",
|
|
85
|
+
githubClientId: "Ov23liWF7NvdZ056RV5J",
|
|
86
|
+
grafanaUrl: "",
|
|
87
|
+
// For grafana tracing
|
|
88
|
+
grafanaAppName: "Symbols Upcoming",
|
|
89
|
+
typesenseCollectionName: "docs",
|
|
90
|
+
typesenseApiKey: "awmcVpbWqZi9IUgmvslp1C5LKDU8tMjA",
|
|
91
|
+
typesenseHost: "tl2qpnwxev4cjm36p-1.a1.typesense.net",
|
|
92
|
+
typesensePort: "443",
|
|
93
|
+
typesenseProtocol: "https"
|
|
94
|
+
},
|
|
95
|
+
staging: {
|
|
96
|
+
socketUrl: "https://staging.api.symbols.app",
|
|
97
|
+
apiUrl: "https://staging.api.symbols.app",
|
|
98
|
+
basedEnv: "staging",
|
|
99
|
+
basedProject: "platform-v2-sm",
|
|
100
|
+
basedOrg: "symbols",
|
|
101
|
+
githubClientId: "Ov23ligwZDQVD0VfuWNa",
|
|
102
|
+
grafanaUrl: "",
|
|
103
|
+
// For grafana tracing
|
|
104
|
+
grafanaAppName: "Symbols Staging",
|
|
105
|
+
typesenseCollectionName: "docs",
|
|
106
|
+
typesenseApiKey: "awmcVpbWqZi9IUgmvslp1C5LKDU8tMjA",
|
|
107
|
+
typesenseHost: "tl2qpnwxev4cjm36p-1.a1.typesense.net",
|
|
108
|
+
typesensePort: "443",
|
|
109
|
+
typesenseProtocol: "https"
|
|
110
|
+
},
|
|
111
|
+
preview: {
|
|
112
|
+
socketUrl: "https://api.symbols.app",
|
|
113
|
+
apiUrl: "https://api.symbols.app",
|
|
114
|
+
basedEnv: "production",
|
|
115
|
+
basedProject: "platform-v2-sm",
|
|
116
|
+
basedOrg: "symbols",
|
|
117
|
+
githubClientId: "Ov23liFAlOEIXtX3dBtR",
|
|
118
|
+
grafanaUrl: "https://faro-collector-prod-us-east-0.grafana.net/collect/5c1089f3c3eea4ec5658e05c3f53baae",
|
|
119
|
+
// For grafana tracing
|
|
120
|
+
grafanaAppName: "Symbols Preview",
|
|
121
|
+
typesenseCollectionName: "docs",
|
|
122
|
+
typesenseApiKey: "awmcVpbWqZi9IUgmvslp1C5LKDU8tMjA",
|
|
123
|
+
typesenseHost: "tl2qpnwxev4cjm36p-1.a1.typesense.net",
|
|
124
|
+
typesensePort: "443",
|
|
125
|
+
typesenseProtocol: "https"
|
|
126
|
+
},
|
|
127
|
+
production: {
|
|
128
|
+
socketUrl: "https://api.symbols.app",
|
|
129
|
+
apiUrl: "https://api.symbols.app",
|
|
130
|
+
basedEnv: "production",
|
|
131
|
+
basedProject: "platform-v2-sm",
|
|
132
|
+
basedOrg: "symbols",
|
|
133
|
+
githubClientId: "Ov23liFAlOEIXtX3dBtR",
|
|
134
|
+
grafanaUrl: "https://faro-collector-prod-us-east-0.grafana.net/collect/5c1089f3c3eea4ec5658e05c3f53baae",
|
|
135
|
+
// For grafana tracing
|
|
136
|
+
grafanaAppName: "Symbols",
|
|
137
|
+
typesenseCollectionName: "docs",
|
|
138
|
+
typesenseApiKey: "awmcVpbWqZi9IUgmvslp1C5LKDU8tMjA",
|
|
139
|
+
typesenseHost: "tl2qpnwxev4cjm36p-1.a1.typesense.net",
|
|
140
|
+
typesensePort: "443",
|
|
141
|
+
typesenseProtocol: "https"
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
var getEnvironment = () => {
|
|
145
|
+
const env = process.env.SYMBOLS_APP_ENV || process.env.NODE_ENV;
|
|
146
|
+
if (!CONFIG[env]) {
|
|
147
|
+
throw new Error(`Unknown environment "${env}"`);
|
|
148
|
+
}
|
|
149
|
+
return env;
|
|
150
|
+
};
|
|
151
|
+
var getConfig = () => {
|
|
152
|
+
try {
|
|
153
|
+
const env = getEnvironment();
|
|
154
|
+
const envConfig = { ...CONFIG.common, ...CONFIG[env] };
|
|
155
|
+
const finalConfig = {
|
|
156
|
+
...envConfig,
|
|
157
|
+
// Deep-merge feature flags so env-specific overrides don't drop common defaults
|
|
158
|
+
features: {
|
|
159
|
+
...CONFIG.common.features || {},
|
|
160
|
+
...CONFIG[env] && CONFIG[env].features || {}
|
|
161
|
+
},
|
|
162
|
+
socketUrl: process.env.SYMBOLS_APP_SOCKET_URL || envConfig.socketUrl,
|
|
163
|
+
apiUrl: process.env.SYMBOLS_APP_API_URL || envConfig.apiUrl,
|
|
164
|
+
basedEnv: process.env.SYMBOLS_APP_BASED_ENV || envConfig.basedEnv,
|
|
165
|
+
basedProject: process.env.SYMBOLS_APP_BASED_PROJECT || envConfig.basedProject,
|
|
166
|
+
basedOrg: process.env.SYMBOLS_APP_BASED_ORG || envConfig.basedOrg,
|
|
167
|
+
githubClientId: process.env.SYMBOLS_APP_GITHUB_CLIENT_ID || envConfig.githubClientId,
|
|
168
|
+
grafanaUrl: process.env.SYMBOLS_APP_GRAFANA_URL || envConfig.grafanaUrl,
|
|
169
|
+
typesenseCollectionName: process.env.TYPESENSE_COLLECTION_NAME || envConfig.typesenseCollectionName,
|
|
170
|
+
typesenseApiKey: process.env.TYPESENSE_API_KEY || envConfig.typesenseApiKey,
|
|
171
|
+
typesenseHost: process.env.TYPESENSE_HOST || envConfig.typesenseHost,
|
|
172
|
+
typesensePort: process.env.TYPESENSE_PORT || envConfig.typesensePort,
|
|
173
|
+
typesenseProtocol: process.env.TYPESENSE_PROTOCOL || envConfig.typesenseProtocol,
|
|
174
|
+
isDevelopment: isDevelopment(env),
|
|
175
|
+
isTesting: env === "testing",
|
|
176
|
+
isStaging: env === "staging",
|
|
177
|
+
isPreview: env === "preview",
|
|
178
|
+
isProduction: env === "production"
|
|
179
|
+
// Store all environment variables for potential future use
|
|
180
|
+
};
|
|
181
|
+
const requiredFields = [
|
|
182
|
+
"socketUrl",
|
|
183
|
+
"apiUrl",
|
|
184
|
+
"githubClientId",
|
|
185
|
+
"googleClientId"
|
|
186
|
+
];
|
|
187
|
+
const missingFields = requiredFields.filter((field) => !finalConfig[field]);
|
|
188
|
+
if (missingFields.length > 0) {
|
|
189
|
+
console.error(
|
|
190
|
+
`Missing required configuration: ${missingFields.join(", ")}`
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
if (finalConfig.isDevelopment) {
|
|
194
|
+
console.warn(
|
|
195
|
+
"environment in SDK:",
|
|
196
|
+
env || process.env.NODE_ENV || process.env.NODE_ENV
|
|
197
|
+
);
|
|
198
|
+
console.log(finalConfig);
|
|
199
|
+
} else if (global.window) {
|
|
200
|
+
global.window.finalConfig = finalConfig;
|
|
201
|
+
}
|
|
202
|
+
return finalConfig;
|
|
203
|
+
} catch (error) {
|
|
204
|
+
console.error("Failed to load environment configuration:", error);
|
|
205
|
+
return {
|
|
206
|
+
...CONFIG.development
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
};
|
|
210
|
+
var environment_default = getConfig();
|
|
211
|
+
|
|
212
|
+
// src/utils/TokenManager.js
|
|
213
|
+
var TokenManager = class {
|
|
214
|
+
constructor(options = {}) {
|
|
215
|
+
/**
|
|
216
|
+
* Memory storage fallback for server-side rendering
|
|
217
|
+
*/
|
|
218
|
+
__publicField(this, "_memoryStorage", {
|
|
219
|
+
_data: {},
|
|
220
|
+
getItem: (key) => this._memoryStorage._data[key] || null,
|
|
221
|
+
setItem: (key, value) => {
|
|
222
|
+
this._memoryStorage._data[key] = value;
|
|
223
|
+
},
|
|
224
|
+
removeItem: (key) => {
|
|
225
|
+
delete this._memoryStorage._data[key];
|
|
226
|
+
},
|
|
227
|
+
clear: () => {
|
|
228
|
+
this._memoryStorage._data = {};
|
|
229
|
+
}
|
|
230
|
+
});
|
|
231
|
+
this.config = {
|
|
232
|
+
storagePrefix: "symbols_",
|
|
233
|
+
storageType: typeof window === "undefined" || process.env.NODE_ENV === "test" || process.env.NODE_ENV === "testing" ? "memory" : "localStorage",
|
|
234
|
+
// 'localStorage' | 'sessionStorage' | 'memory'
|
|
235
|
+
refreshBuffer: 60 * 1e3,
|
|
236
|
+
// Refresh 1 minute before expiry
|
|
237
|
+
maxRetries: 3,
|
|
238
|
+
apiUrl: options.apiUrl || "/api",
|
|
239
|
+
onTokenRefresh: options.onTokenRefresh || null,
|
|
240
|
+
onTokenExpired: options.onTokenExpired || null,
|
|
241
|
+
onTokenError: options.onTokenError || null,
|
|
242
|
+
...options
|
|
243
|
+
};
|
|
244
|
+
this.tokens = {
|
|
245
|
+
accessToken: null,
|
|
246
|
+
refreshToken: null,
|
|
247
|
+
expiresAt: null,
|
|
248
|
+
expiresIn: null
|
|
249
|
+
};
|
|
250
|
+
this.refreshPromise = null;
|
|
251
|
+
this.refreshTimeout = null;
|
|
252
|
+
this.retryCount = 0;
|
|
253
|
+
this.loadTokens();
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* Storage keys
|
|
257
|
+
*/
|
|
258
|
+
get storageKeys() {
|
|
259
|
+
return {
|
|
260
|
+
accessToken: `${this.config.storagePrefix}access_token`,
|
|
261
|
+
refreshToken: `${this.config.storagePrefix}refresh_token`,
|
|
262
|
+
expiresAt: `${this.config.storagePrefix}expires_at`,
|
|
263
|
+
expiresIn: `${this.config.storagePrefix}expires_in`
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* Get storage instance based on configuration
|
|
268
|
+
*/
|
|
269
|
+
get storage() {
|
|
270
|
+
if (typeof window === "undefined") {
|
|
271
|
+
return this._memoryStorage;
|
|
272
|
+
}
|
|
273
|
+
const safeGetStorage = (provider) => {
|
|
274
|
+
try {
|
|
275
|
+
const storage = provider();
|
|
276
|
+
const testKey = `${this.config.storagePrefix}__tm_test__`;
|
|
277
|
+
storage.setItem(testKey, "1");
|
|
278
|
+
storage.removeItem(testKey);
|
|
279
|
+
return storage;
|
|
280
|
+
} catch {
|
|
281
|
+
return null;
|
|
282
|
+
}
|
|
283
|
+
};
|
|
284
|
+
const localStorageInstance = safeGetStorage(() => window.localStorage);
|
|
285
|
+
const sessionStorageInstance = safeGetStorage(() => window.sessionStorage);
|
|
286
|
+
switch (this.config.storageType) {
|
|
287
|
+
case "sessionStorage":
|
|
288
|
+
return sessionStorageInstance || this._memoryStorage;
|
|
289
|
+
case "memory":
|
|
290
|
+
return this._memoryStorage;
|
|
291
|
+
default:
|
|
292
|
+
return localStorageInstance || this._memoryStorage;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
/**
|
|
296
|
+
* Set tokens and persist to storage
|
|
297
|
+
*/
|
|
298
|
+
setTokens(tokenData) {
|
|
299
|
+
const {
|
|
300
|
+
access_token: accessToken,
|
|
301
|
+
refresh_token: refreshToken,
|
|
302
|
+
expires_in: expiresIn,
|
|
303
|
+
token_type: tokenType = "Bearer"
|
|
304
|
+
} = tokenData;
|
|
305
|
+
if (!accessToken) {
|
|
306
|
+
throw new Error("Access token is required");
|
|
307
|
+
}
|
|
308
|
+
const now = Date.now();
|
|
309
|
+
const expiresAt = expiresIn ? now + expiresIn * 1e3 : null;
|
|
310
|
+
this.tokens = {
|
|
311
|
+
accessToken,
|
|
312
|
+
refreshToken: refreshToken || this.tokens.refreshToken,
|
|
313
|
+
expiresAt,
|
|
314
|
+
expiresIn,
|
|
315
|
+
tokenType
|
|
316
|
+
};
|
|
317
|
+
this.saveTokens();
|
|
318
|
+
this.scheduleRefresh();
|
|
319
|
+
if (this.config.onTokenRefresh) {
|
|
320
|
+
this.config.onTokenRefresh(this.tokens);
|
|
321
|
+
}
|
|
322
|
+
return this.tokens;
|
|
323
|
+
}
|
|
324
|
+
/**
|
|
325
|
+
* Get current access token
|
|
326
|
+
*/
|
|
327
|
+
getAccessToken() {
|
|
328
|
+
return this.tokens.accessToken;
|
|
329
|
+
}
|
|
330
|
+
/**
|
|
331
|
+
* Get current refresh token
|
|
332
|
+
*/
|
|
333
|
+
getRefreshToken() {
|
|
334
|
+
return this.tokens.refreshToken;
|
|
335
|
+
}
|
|
336
|
+
/**
|
|
337
|
+
* Get authorization header value
|
|
338
|
+
*/
|
|
339
|
+
getAuthHeader() {
|
|
340
|
+
const token = this.getAccessToken();
|
|
341
|
+
if (!token) {
|
|
342
|
+
return null;
|
|
343
|
+
}
|
|
344
|
+
return `${this.tokens.tokenType || "Bearer"} ${token}`;
|
|
345
|
+
}
|
|
346
|
+
/**
|
|
347
|
+
* Check if access token is valid and not expired
|
|
348
|
+
*/
|
|
349
|
+
isAccessTokenValid() {
|
|
350
|
+
if (!this.tokens.accessToken) {
|
|
351
|
+
return false;
|
|
352
|
+
}
|
|
353
|
+
if (!this.tokens.expiresAt) {
|
|
354
|
+
return true;
|
|
355
|
+
}
|
|
356
|
+
const now = Date.now();
|
|
357
|
+
const isValid = now < this.tokens.expiresAt - this.config.refreshBuffer;
|
|
358
|
+
if (!isValid) {
|
|
359
|
+
console.log("[TokenManager] Access token is expired or near expiry:", {
|
|
360
|
+
now: new Date(now).toISOString(),
|
|
361
|
+
expiresAt: new Date(this.tokens.expiresAt).toISOString(),
|
|
362
|
+
refreshBuffer: this.config.refreshBuffer
|
|
363
|
+
});
|
|
364
|
+
}
|
|
365
|
+
return isValid;
|
|
366
|
+
}
|
|
367
|
+
/**
|
|
368
|
+
* Check if access token exists and is not expired (without refresh buffer)
|
|
369
|
+
*/
|
|
370
|
+
isAccessTokenActuallyValid() {
|
|
371
|
+
if (!this.tokens.accessToken) {
|
|
372
|
+
return false;
|
|
373
|
+
}
|
|
374
|
+
if (!this.tokens.expiresAt) {
|
|
375
|
+
return true;
|
|
376
|
+
}
|
|
377
|
+
const now = Date.now();
|
|
378
|
+
return now < this.tokens.expiresAt;
|
|
379
|
+
}
|
|
380
|
+
/**
|
|
381
|
+
* Check if tokens exist (regardless of expiry)
|
|
382
|
+
*/
|
|
383
|
+
hasTokens() {
|
|
384
|
+
return Boolean(this.tokens.accessToken);
|
|
385
|
+
}
|
|
386
|
+
/**
|
|
387
|
+
* Check if refresh token exists
|
|
388
|
+
*/
|
|
389
|
+
hasRefreshToken() {
|
|
390
|
+
return Boolean(this.tokens.refreshToken);
|
|
391
|
+
}
|
|
392
|
+
/**
|
|
393
|
+
* Automatically refresh tokens if needed
|
|
394
|
+
*/
|
|
395
|
+
async ensureValidToken() {
|
|
396
|
+
if (!this.hasTokens()) {
|
|
397
|
+
return null;
|
|
398
|
+
}
|
|
399
|
+
if (this.isAccessTokenValid()) {
|
|
400
|
+
return this.getAccessToken();
|
|
401
|
+
}
|
|
402
|
+
if (!this.hasRefreshToken()) {
|
|
403
|
+
this.clearTokens();
|
|
404
|
+
if (this.config.onTokenExpired) {
|
|
405
|
+
this.config.onTokenExpired();
|
|
406
|
+
}
|
|
407
|
+
return null;
|
|
408
|
+
}
|
|
409
|
+
try {
|
|
410
|
+
await this.refreshTokens();
|
|
411
|
+
return this.getAccessToken();
|
|
412
|
+
} catch (error) {
|
|
413
|
+
this.clearTokens();
|
|
414
|
+
if (this.config.onTokenError) {
|
|
415
|
+
this.config.onTokenError(error);
|
|
416
|
+
}
|
|
417
|
+
throw error;
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
/**
|
|
421
|
+
* Refresh access token using refresh token
|
|
422
|
+
*/
|
|
423
|
+
async refreshTokens() {
|
|
424
|
+
if (this.refreshPromise) {
|
|
425
|
+
return this.refreshPromise;
|
|
426
|
+
}
|
|
427
|
+
if (!this.hasRefreshToken()) {
|
|
428
|
+
throw new Error("No refresh token available");
|
|
429
|
+
}
|
|
430
|
+
if (this.retryCount >= this.config.maxRetries) {
|
|
431
|
+
throw new Error("Max refresh retries exceeded");
|
|
432
|
+
}
|
|
433
|
+
this.refreshPromise = this._performRefresh();
|
|
434
|
+
try {
|
|
435
|
+
const result = await this.refreshPromise;
|
|
436
|
+
this.retryCount = 0;
|
|
437
|
+
return result;
|
|
438
|
+
} catch (error) {
|
|
439
|
+
this.retryCount++;
|
|
440
|
+
throw error;
|
|
441
|
+
} finally {
|
|
442
|
+
this.refreshPromise = null;
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
/**
|
|
446
|
+
* Perform the actual token refresh request
|
|
447
|
+
*/
|
|
448
|
+
async _performRefresh() {
|
|
449
|
+
var _a;
|
|
450
|
+
const refreshToken = this.getRefreshToken();
|
|
451
|
+
const response = await fetch(`${this.config.apiUrl}/core/auth/refresh`, {
|
|
452
|
+
method: "POST",
|
|
453
|
+
headers: {
|
|
454
|
+
"Content-Type": "application/json"
|
|
455
|
+
},
|
|
456
|
+
body: JSON.stringify({ refreshToken })
|
|
457
|
+
});
|
|
458
|
+
if (!response.ok) {
|
|
459
|
+
const errorData = await response.json().catch(() => ({}));
|
|
460
|
+
throw new Error(errorData.message || `Token refresh failed: ${response.status}`);
|
|
461
|
+
}
|
|
462
|
+
const responseData = await response.json();
|
|
463
|
+
if (responseData.success && responseData.data && responseData.data.tokens) {
|
|
464
|
+
const { tokens } = responseData.data;
|
|
465
|
+
const tokenData = {
|
|
466
|
+
access_token: tokens.accessToken,
|
|
467
|
+
refresh_token: tokens.refreshToken,
|
|
468
|
+
expires_in: (_a = tokens.accessTokenExp) == null ? void 0 : _a.expiresIn,
|
|
469
|
+
token_type: "Bearer"
|
|
470
|
+
};
|
|
471
|
+
return this.setTokens(tokenData);
|
|
472
|
+
}
|
|
473
|
+
return this.setTokens(responseData);
|
|
474
|
+
}
|
|
475
|
+
/**
|
|
476
|
+
* Schedule automatic token refresh
|
|
477
|
+
*/
|
|
478
|
+
scheduleRefresh() {
|
|
479
|
+
if (this.refreshTimeout) {
|
|
480
|
+
clearTimeout(this.refreshTimeout);
|
|
481
|
+
this.refreshTimeout = null;
|
|
482
|
+
}
|
|
483
|
+
if (!this.tokens.expiresAt || !this.hasRefreshToken()) {
|
|
484
|
+
return;
|
|
485
|
+
}
|
|
486
|
+
const now = Date.now();
|
|
487
|
+
const refreshTime = this.tokens.expiresAt - this.config.refreshBuffer;
|
|
488
|
+
const delay = Math.max(0, refreshTime - now);
|
|
489
|
+
this.refreshTimeout = setTimeout(async () => {
|
|
490
|
+
try {
|
|
491
|
+
await this.refreshTokens();
|
|
492
|
+
} catch (error) {
|
|
493
|
+
console.error("Automatic token refresh failed:", error);
|
|
494
|
+
if (this.config.onTokenError) {
|
|
495
|
+
this.config.onTokenError(error);
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
}, delay);
|
|
499
|
+
}
|
|
500
|
+
/**
|
|
501
|
+
* Save tokens to storage
|
|
502
|
+
*/
|
|
503
|
+
saveTokens() {
|
|
504
|
+
try {
|
|
505
|
+
const { storage } = this;
|
|
506
|
+
const keys = this.storageKeys;
|
|
507
|
+
if (this.tokens.accessToken) {
|
|
508
|
+
storage.setItem(keys.accessToken, this.tokens.accessToken);
|
|
509
|
+
}
|
|
510
|
+
if (this.tokens.refreshToken) {
|
|
511
|
+
storage.setItem(keys.refreshToken, this.tokens.refreshToken);
|
|
512
|
+
}
|
|
513
|
+
if (this.tokens.expiresAt) {
|
|
514
|
+
storage.setItem(keys.expiresAt, this.tokens.expiresAt.toString());
|
|
515
|
+
}
|
|
516
|
+
if (this.tokens.expiresIn) {
|
|
517
|
+
storage.setItem(keys.expiresIn, this.tokens.expiresIn.toString());
|
|
518
|
+
}
|
|
519
|
+
} catch (error) {
|
|
520
|
+
console.error("[TokenManager] Error saving tokens to storage:", error);
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
/**
|
|
524
|
+
* Load tokens from storage
|
|
525
|
+
*/
|
|
526
|
+
loadTokens() {
|
|
527
|
+
try {
|
|
528
|
+
const { storage } = this;
|
|
529
|
+
const keys = this.storageKeys;
|
|
530
|
+
const accessToken = storage.getItem(keys.accessToken);
|
|
531
|
+
const refreshToken = storage.getItem(keys.refreshToken);
|
|
532
|
+
const expiresAt = storage.getItem(keys.expiresAt);
|
|
533
|
+
const expiresIn = storage.getItem(keys.expiresIn);
|
|
534
|
+
if (accessToken) {
|
|
535
|
+
this.tokens = {
|
|
536
|
+
accessToken,
|
|
537
|
+
refreshToken,
|
|
538
|
+
expiresAt: expiresAt ? parseInt(expiresAt, 10) : null,
|
|
539
|
+
expiresIn: expiresIn ? parseInt(expiresIn, 10) : null,
|
|
540
|
+
tokenType: "Bearer"
|
|
541
|
+
};
|
|
542
|
+
this.scheduleRefresh();
|
|
543
|
+
}
|
|
544
|
+
} catch (error) {
|
|
545
|
+
console.error("[TokenManager] Error loading tokens from storage:", error);
|
|
546
|
+
this.tokens = {
|
|
547
|
+
accessToken: null,
|
|
548
|
+
refreshToken: null,
|
|
549
|
+
expiresAt: null,
|
|
550
|
+
expiresIn: null
|
|
551
|
+
};
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
/**
|
|
555
|
+
* Clear all tokens
|
|
556
|
+
*/
|
|
557
|
+
clearTokens() {
|
|
558
|
+
this.tokens = {
|
|
559
|
+
accessToken: null,
|
|
560
|
+
refreshToken: null,
|
|
561
|
+
expiresAt: null,
|
|
562
|
+
expiresIn: null
|
|
563
|
+
};
|
|
564
|
+
const { storage } = this;
|
|
565
|
+
const keys = this.storageKeys;
|
|
566
|
+
Object.values(keys).forEach((key) => {
|
|
567
|
+
storage.removeItem(key);
|
|
568
|
+
});
|
|
569
|
+
if (this.refreshTimeout) {
|
|
570
|
+
clearTimeout(this.refreshTimeout);
|
|
571
|
+
this.refreshTimeout = null;
|
|
572
|
+
}
|
|
573
|
+
this.retryCount = 0;
|
|
574
|
+
}
|
|
575
|
+
/**
|
|
576
|
+
* Get token status information
|
|
577
|
+
*/
|
|
578
|
+
getTokenStatus() {
|
|
579
|
+
const hasTokens = this.hasTokens();
|
|
580
|
+
const isValid = this.isAccessTokenValid();
|
|
581
|
+
const { expiresAt } = this.tokens;
|
|
582
|
+
const timeToExpiry = expiresAt ? expiresAt - Date.now() : null;
|
|
583
|
+
return {
|
|
584
|
+
hasTokens,
|
|
585
|
+
isValid,
|
|
586
|
+
hasRefreshToken: this.hasRefreshToken(),
|
|
587
|
+
expiresAt,
|
|
588
|
+
timeToExpiry,
|
|
589
|
+
willExpireSoon: timeToExpiry ? timeToExpiry < this.config.refreshBuffer : false
|
|
590
|
+
};
|
|
591
|
+
}
|
|
592
|
+
/**
|
|
593
|
+
* Cleanup resources
|
|
594
|
+
*/
|
|
595
|
+
destroy() {
|
|
596
|
+
if (this.refreshTimeout) {
|
|
597
|
+
clearTimeout(this.refreshTimeout);
|
|
598
|
+
this.refreshTimeout = null;
|
|
599
|
+
}
|
|
600
|
+
this.refreshPromise = null;
|
|
601
|
+
}
|
|
602
|
+
};
|
|
603
|
+
var defaultTokenManager = null;
|
|
604
|
+
var getTokenManager = (options) => {
|
|
605
|
+
if (!defaultTokenManager) {
|
|
606
|
+
defaultTokenManager = new TokenManager(options);
|
|
607
|
+
}
|
|
608
|
+
return defaultTokenManager;
|
|
609
|
+
};
|
|
610
|
+
|
|
1
611
|
// src/services/BaseService.js
|
|
2
612
|
var BaseService = class {
|
|
3
613
|
constructor({ context, options } = {}) {
|
|
@@ -5,14 +615,34 @@ var BaseService = class {
|
|
|
5
615
|
this._options = options || {};
|
|
6
616
|
this._ready = false;
|
|
7
617
|
this._error = null;
|
|
618
|
+
this._apiUrl = null;
|
|
619
|
+
this._tokenManager = null;
|
|
8
620
|
}
|
|
9
621
|
// Initialize service
|
|
10
|
-
init() {
|
|
11
|
-
|
|
622
|
+
init({ context }) {
|
|
623
|
+
try {
|
|
624
|
+
const { apiUrl } = context || this._context;
|
|
625
|
+
this._apiUrl = apiUrl || environment_default.apiUrl;
|
|
626
|
+
if (!this._apiUrl) {
|
|
627
|
+
throw new Error("Service base URL not configured");
|
|
628
|
+
}
|
|
629
|
+
this._tokenManager = getTokenManager({
|
|
630
|
+
apiUrl: this._apiUrl,
|
|
631
|
+
onTokenError: (error) => {
|
|
632
|
+
console.error("Token management error:", error);
|
|
633
|
+
}
|
|
634
|
+
});
|
|
635
|
+
this._setReady();
|
|
636
|
+
} catch (error) {
|
|
637
|
+
this._setError(error);
|
|
638
|
+
throw error;
|
|
639
|
+
}
|
|
12
640
|
}
|
|
13
641
|
// Update context
|
|
14
642
|
updateContext(context) {
|
|
15
|
-
|
|
643
|
+
if (context && typeof context === "object") {
|
|
644
|
+
Object.assign(this._context, context);
|
|
645
|
+
}
|
|
16
646
|
}
|
|
17
647
|
// Get service status
|
|
18
648
|
getStatus() {
|
|
@@ -35,96 +665,139 @@ var BaseService = class {
|
|
|
35
665
|
this._ready = false;
|
|
36
666
|
this._error = error;
|
|
37
667
|
}
|
|
668
|
+
_getTrackingService() {
|
|
669
|
+
var _a;
|
|
670
|
+
const services = (_a = this._context) == null ? void 0 : _a.services;
|
|
671
|
+
const tracking = services == null ? void 0 : services.tracking;
|
|
672
|
+
if (!tracking || typeof tracking.trackError !== "function") {
|
|
673
|
+
return null;
|
|
674
|
+
}
|
|
675
|
+
return tracking;
|
|
676
|
+
}
|
|
677
|
+
_shouldTrackErrors() {
|
|
678
|
+
var _a;
|
|
679
|
+
const name = (_a = this == null ? void 0 : this.constructor) == null ? void 0 : _a.name;
|
|
680
|
+
return name !== "TrackingService";
|
|
681
|
+
}
|
|
682
|
+
_trackServiceError(error, details = {}) {
|
|
683
|
+
var _a;
|
|
684
|
+
if (!this._shouldTrackErrors()) {
|
|
685
|
+
return;
|
|
686
|
+
}
|
|
687
|
+
try {
|
|
688
|
+
const tracking = this._getTrackingService();
|
|
689
|
+
if (!tracking) {
|
|
690
|
+
return;
|
|
691
|
+
}
|
|
692
|
+
const context = {
|
|
693
|
+
service: ((_a = this == null ? void 0 : this.constructor) == null ? void 0 : _a.name) || "UnknownService",
|
|
694
|
+
apiUrl: this._apiUrl || null,
|
|
695
|
+
...details
|
|
696
|
+
};
|
|
697
|
+
tracking.trackError(error instanceof Error ? error : new Error(String(error)), context);
|
|
698
|
+
} catch {
|
|
699
|
+
}
|
|
700
|
+
}
|
|
38
701
|
_requireAuth() {
|
|
39
|
-
if (!this.
|
|
702
|
+
if (!this.getAuthToken()) {
|
|
40
703
|
throw new Error("Authentication required");
|
|
41
704
|
}
|
|
42
705
|
}
|
|
43
|
-
_requireReady() {
|
|
706
|
+
_requireReady(methodName = "unknown") {
|
|
44
707
|
if (!this.isReady()) {
|
|
45
|
-
throw new Error(
|
|
708
|
+
throw new Error(`Service not initialized for method: ${methodName}`);
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
// Shared HTTP request method
|
|
712
|
+
async _request(endpoint, options = {}) {
|
|
713
|
+
const url = `${this._apiUrl}/core${endpoint}`;
|
|
714
|
+
const defaultHeaders = {};
|
|
715
|
+
if (!(options.body instanceof FormData)) {
|
|
716
|
+
defaultHeaders["Content-Type"] = "application/json";
|
|
717
|
+
}
|
|
718
|
+
if (this._requiresInit(options.methodName) && this._tokenManager) {
|
|
719
|
+
try {
|
|
720
|
+
const validToken = await this._tokenManager.ensureValidToken();
|
|
721
|
+
if (validToken) {
|
|
722
|
+
const authHeader = this._tokenManager.getAuthHeader();
|
|
723
|
+
if (authHeader) {
|
|
724
|
+
defaultHeaders.Authorization = authHeader;
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
} catch (error) {
|
|
728
|
+
console.warn(
|
|
729
|
+
"Token management failed, proceeding without authentication:",
|
|
730
|
+
error
|
|
731
|
+
);
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
try {
|
|
735
|
+
const response = await fetch(url, {
|
|
736
|
+
...options,
|
|
737
|
+
headers: {
|
|
738
|
+
...defaultHeaders,
|
|
739
|
+
...options.headers
|
|
740
|
+
}
|
|
741
|
+
});
|
|
742
|
+
if (!response.ok) {
|
|
743
|
+
let error = {
|
|
744
|
+
message: `HTTP ${response.status}: ${response.statusText}`
|
|
745
|
+
};
|
|
746
|
+
try {
|
|
747
|
+
error = await response.json();
|
|
748
|
+
} catch {
|
|
749
|
+
}
|
|
750
|
+
this._trackServiceError(
|
|
751
|
+
new Error(error.message || error.error || `HTTP ${response.status}: ${response.statusText}`),
|
|
752
|
+
{
|
|
753
|
+
endpoint,
|
|
754
|
+
methodName: options.methodName,
|
|
755
|
+
status: response.status,
|
|
756
|
+
statusText: response.statusText
|
|
757
|
+
}
|
|
758
|
+
);
|
|
759
|
+
throw new Error(error.message || error.error || "Request failed", { cause: error });
|
|
760
|
+
}
|
|
761
|
+
return response.status === 204 ? null : response.json();
|
|
762
|
+
} catch (error) {
|
|
763
|
+
this._trackServiceError(error, {
|
|
764
|
+
endpoint,
|
|
765
|
+
methodName: options.methodName
|
|
766
|
+
});
|
|
767
|
+
throw new Error(`Request failed: ${error.message}`, { cause: error });
|
|
46
768
|
}
|
|
47
769
|
}
|
|
770
|
+
// Helper method to determine if a method requires initialization
|
|
771
|
+
_requiresInit(methodName) {
|
|
772
|
+
const noInitMethods = /* @__PURE__ */ new Set([
|
|
773
|
+
"register",
|
|
774
|
+
"login",
|
|
775
|
+
"googleAuth",
|
|
776
|
+
"googleAuthCallback",
|
|
777
|
+
"githubAuth",
|
|
778
|
+
"requestPasswordReset",
|
|
779
|
+
"confirmPasswordReset",
|
|
780
|
+
"confirmRegistration",
|
|
781
|
+
"verifyEmail",
|
|
782
|
+
"getPlans",
|
|
783
|
+
"getPlan",
|
|
784
|
+
"listPublicProjects",
|
|
785
|
+
"getPublicProject"
|
|
786
|
+
]);
|
|
787
|
+
return !noInitMethods.has(methodName);
|
|
788
|
+
}
|
|
789
|
+
// Cleanup method
|
|
790
|
+
destroy() {
|
|
791
|
+
if (this._tokenManager) {
|
|
792
|
+
this._tokenManager.destroy();
|
|
793
|
+
this._tokenManager = null;
|
|
794
|
+
}
|
|
795
|
+
this._ready = false;
|
|
796
|
+
this._setReady(false);
|
|
797
|
+
}
|
|
48
798
|
};
|
|
49
799
|
|
|
50
800
|
// src/utils/permission.js
|
|
51
|
-
var PERMISSION_MAP = {
|
|
52
|
-
// Content & Design Operations
|
|
53
|
-
edit: {
|
|
54
|
-
permissions: ["editMode", "showCode"],
|
|
55
|
-
features: ["editMode"]
|
|
56
|
-
},
|
|
57
|
-
view: {
|
|
58
|
-
permissions: ["showContent", "platformSettings"],
|
|
59
|
-
features: ["canvasPages"]
|
|
60
|
-
},
|
|
61
|
-
design: {
|
|
62
|
-
permissions: ["editMode", "showCode"],
|
|
63
|
-
features: ["accessToSymbolsLibrary", "marketplace"]
|
|
64
|
-
},
|
|
65
|
-
// Project Management
|
|
66
|
-
manage: {
|
|
67
|
-
permissions: ["projectSettings", "iam"],
|
|
68
|
-
features: ["workspaceAdministration", "teamRolesAndPermissions"]
|
|
69
|
-
},
|
|
70
|
-
configure: {
|
|
71
|
-
permissions: ["projectSettings", "branchProtection"],
|
|
72
|
-
features: ["customBackendIntegration", "integrationsSDK"]
|
|
73
|
-
},
|
|
74
|
-
invite: {
|
|
75
|
-
permissions: ["inviteMembers"],
|
|
76
|
-
features: ["inviteMembersIAM"]
|
|
77
|
-
},
|
|
78
|
-
// Version Control
|
|
79
|
-
branch: {
|
|
80
|
-
permissions: ["versions", "branchProtection"],
|
|
81
|
-
features: ["branching", "versionHistory"]
|
|
82
|
-
},
|
|
83
|
-
merge: {
|
|
84
|
-
permissions: ["versions", "branchProtection"],
|
|
85
|
-
features: ["branching"]
|
|
86
|
-
},
|
|
87
|
-
// Export & Integration
|
|
88
|
-
export: {
|
|
89
|
-
permissions: ["showCode"],
|
|
90
|
-
features: ["downloadAsSVG", "downloadAsReact", "exportToFigma"]
|
|
91
|
-
},
|
|
92
|
-
import: {
|
|
93
|
-
permissions: ["editMode"],
|
|
94
|
-
features: ["importFromFigma"]
|
|
95
|
-
},
|
|
96
|
-
// AI Features
|
|
97
|
-
aiCopilot: {
|
|
98
|
-
permissions: ["editMode"],
|
|
99
|
-
features: ["aiCopilot:3", "aiCopilot:5", "aiCopilot:15"]
|
|
100
|
-
},
|
|
101
|
-
aiChatbot: {
|
|
102
|
-
permissions: ["showContent"],
|
|
103
|
-
features: ["aiChatbot:3", "aiChatbot:5", "aiChatbot:15"]
|
|
104
|
-
},
|
|
105
|
-
// Advanced Features
|
|
106
|
-
analytics: {
|
|
107
|
-
permissions: ["projectSettings"],
|
|
108
|
-
features: ["analytics", "crashalytics"]
|
|
109
|
-
},
|
|
110
|
-
payment: {
|
|
111
|
-
permissions: ["projectSettings", "iam"],
|
|
112
|
-
features: ["stripeConnect"]
|
|
113
|
-
},
|
|
114
|
-
deployment: {
|
|
115
|
-
permissions: ["projectSettings"],
|
|
116
|
-
features: ["dompilerCloud", "customDomain"]
|
|
117
|
-
},
|
|
118
|
-
// Documentation & Sharing
|
|
119
|
-
docs: {
|
|
120
|
-
permissions: ["showContent"],
|
|
121
|
-
features: ["autoDesignDocs"]
|
|
122
|
-
},
|
|
123
|
-
share: {
|
|
124
|
-
permissions: ["inviteMembers"],
|
|
125
|
-
features: ["sharing", "sharedLibraries"]
|
|
126
|
-
}
|
|
127
|
-
};
|
|
128
801
|
var ROLE_PERMISSIONS = {
|
|
129
802
|
guest: ["viewPublicProjects"],
|
|
130
803
|
user: ["viewPublicProjects"],
|
|
@@ -261,187 +934,742 @@ var PROJECT_ROLE_PERMISSIONS = {
|
|
|
261
934
|
};
|
|
262
935
|
|
|
263
936
|
// src/services/AuthService.js
|
|
937
|
+
var PLUGIN_SESSION_STORAGE_KEY = "plugin_auth_session";
|
|
264
938
|
var AuthService = class extends BaseService {
|
|
265
939
|
constructor(config) {
|
|
940
|
+
var _a, _b;
|
|
266
941
|
super(config);
|
|
267
942
|
this._userRoles = /* @__PURE__ */ new Set(["guest", "editor", "admin", "owner"]);
|
|
268
943
|
this._projectTiers = /* @__PURE__ */ new Set([
|
|
269
944
|
"ready",
|
|
270
|
-
"
|
|
945
|
+
"starter",
|
|
271
946
|
"pro1",
|
|
272
947
|
"pro2",
|
|
273
948
|
"enterprise"
|
|
274
949
|
]);
|
|
275
|
-
this.
|
|
950
|
+
this._projectRoleCache = /* @__PURE__ */ new Map();
|
|
951
|
+
this._roleCacheExpiry = 5 * 60 * 1e3;
|
|
952
|
+
this._pluginSession = null;
|
|
953
|
+
this._resolvePluginSession(
|
|
954
|
+
(config == null ? void 0 : config.session) || (config == null ? void 0 : config.pluginSession) || ((_a = config == null ? void 0 : config.options) == null ? void 0 : _a.pluginSession) || ((_b = config == null ? void 0 : config.context) == null ? void 0 : _b.pluginSession) || null
|
|
955
|
+
);
|
|
276
956
|
}
|
|
277
|
-
//
|
|
278
|
-
|
|
957
|
+
// Use BaseService.init/_request/_requireReady implementations
|
|
958
|
+
// ==================== AUTH METHODS ====================
|
|
959
|
+
async register(userData, options = {}) {
|
|
279
960
|
try {
|
|
280
|
-
const {
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
961
|
+
const { payload, session } = this._preparePluginPayload(
|
|
962
|
+
{ ...userData || {} },
|
|
963
|
+
options.session
|
|
964
|
+
);
|
|
965
|
+
const response = await this._request("/auth/register", {
|
|
966
|
+
method: "POST",
|
|
967
|
+
body: JSON.stringify(payload),
|
|
968
|
+
methodName: "register"
|
|
969
|
+
});
|
|
970
|
+
if (response.success) {
|
|
971
|
+
if (session) {
|
|
972
|
+
this._clearPluginSession(session);
|
|
286
973
|
}
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
974
|
+
return response.data;
|
|
975
|
+
}
|
|
976
|
+
throw new Error(response.message);
|
|
290
977
|
} catch (error) {
|
|
291
|
-
|
|
292
|
-
throw error;
|
|
978
|
+
throw new Error(`Registration failed: ${error.message}`, { cause: error });
|
|
293
979
|
}
|
|
294
980
|
}
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
981
|
+
async login(email, password, options = {}) {
|
|
982
|
+
var _a;
|
|
983
|
+
try {
|
|
984
|
+
const { payload, session } = this._preparePluginPayload(
|
|
985
|
+
{
|
|
986
|
+
email,
|
|
987
|
+
password
|
|
988
|
+
},
|
|
989
|
+
options.session
|
|
990
|
+
);
|
|
991
|
+
const response = await this._request("/auth/login", {
|
|
992
|
+
method: "POST",
|
|
993
|
+
body: JSON.stringify(payload),
|
|
994
|
+
methodName: "login"
|
|
995
|
+
});
|
|
996
|
+
if (response.success && response.data && response.data.tokens) {
|
|
997
|
+
const { tokens } = response.data;
|
|
998
|
+
const tokenData = {
|
|
999
|
+
access_token: tokens.accessToken,
|
|
1000
|
+
refresh_token: tokens.refreshToken,
|
|
1001
|
+
expires_in: (_a = tokens.accessTokenExp) == null ? void 0 : _a.expiresIn,
|
|
1002
|
+
token_type: "Bearer"
|
|
1003
|
+
};
|
|
1004
|
+
if (this._tokenManager) {
|
|
1005
|
+
this._tokenManager.setTokens(tokenData);
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
1008
|
+
if (response.success) {
|
|
1009
|
+
if (session) {
|
|
1010
|
+
this._clearPluginSession(session);
|
|
1011
|
+
}
|
|
1012
|
+
return response.data;
|
|
1013
|
+
}
|
|
1014
|
+
throw new Error(response.message);
|
|
1015
|
+
} catch (error) {
|
|
1016
|
+
throw new Error(`Login failed: ${error.message}`, { cause: error });
|
|
1017
|
+
}
|
|
1018
|
+
}
|
|
1019
|
+
async logout() {
|
|
1020
|
+
this._requireReady("logout");
|
|
1021
|
+
try {
|
|
1022
|
+
await this._request("/auth/logout", {
|
|
1023
|
+
method: "POST",
|
|
1024
|
+
methodName: "logout"
|
|
1025
|
+
});
|
|
1026
|
+
if (this._tokenManager) {
|
|
1027
|
+
this._tokenManager.clearTokens();
|
|
1028
|
+
}
|
|
1029
|
+
} catch (error) {
|
|
1030
|
+
if (this._tokenManager) {
|
|
1031
|
+
this._tokenManager.clearTokens();
|
|
1032
|
+
}
|
|
1033
|
+
throw new Error(`Logout failed: ${error.message}`, { cause: error });
|
|
1034
|
+
}
|
|
307
1035
|
}
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
1036
|
+
async refreshToken(refreshToken) {
|
|
1037
|
+
try {
|
|
1038
|
+
const response = await this._request("/auth/refresh", {
|
|
1039
|
+
method: "POST",
|
|
1040
|
+
body: JSON.stringify({ refreshToken }),
|
|
1041
|
+
methodName: "refreshToken"
|
|
1042
|
+
});
|
|
1043
|
+
if (response.success) {
|
|
1044
|
+
return response.data;
|
|
1045
|
+
}
|
|
1046
|
+
throw new Error(response.message);
|
|
1047
|
+
} catch (error) {
|
|
1048
|
+
throw new Error(`Token refresh failed: ${error.message}`, { cause: error });
|
|
1049
|
+
}
|
|
1050
|
+
}
|
|
1051
|
+
async googleAuth(idToken, inviteToken = null, options = {}) {
|
|
1052
|
+
var _a;
|
|
1053
|
+
try {
|
|
1054
|
+
const { payload, session } = this._preparePluginPayload({ idToken }, options.session);
|
|
1055
|
+
if (inviteToken) {
|
|
1056
|
+
payload.inviteToken = inviteToken;
|
|
1057
|
+
}
|
|
1058
|
+
const response = await this._request("/auth/google", {
|
|
1059
|
+
method: "POST",
|
|
1060
|
+
body: JSON.stringify(payload),
|
|
1061
|
+
methodName: "googleAuth"
|
|
1062
|
+
});
|
|
1063
|
+
if (response.success && response.data && response.data.tokens) {
|
|
1064
|
+
const { tokens } = response.data;
|
|
1065
|
+
const tokenData = {
|
|
1066
|
+
access_token: tokens.accessToken,
|
|
1067
|
+
refresh_token: tokens.refreshToken,
|
|
1068
|
+
expires_in: (_a = tokens.accessTokenExp) == null ? void 0 : _a.expiresIn,
|
|
1069
|
+
token_type: "Bearer"
|
|
1070
|
+
};
|
|
1071
|
+
if (this._tokenManager) {
|
|
1072
|
+
this._tokenManager.setTokens(tokenData);
|
|
1073
|
+
}
|
|
1074
|
+
}
|
|
1075
|
+
if (response.success) {
|
|
1076
|
+
if (session) {
|
|
1077
|
+
this._clearPluginSession(session);
|
|
1078
|
+
}
|
|
1079
|
+
return response.data;
|
|
1080
|
+
}
|
|
1081
|
+
throw new Error(response.message);
|
|
1082
|
+
} catch (error) {
|
|
1083
|
+
throw new Error(`Google auth failed: ${error.message}`, { cause: error });
|
|
1084
|
+
}
|
|
1085
|
+
}
|
|
1086
|
+
async githubAuth(code, inviteToken = null, options = {}) {
|
|
1087
|
+
var _a;
|
|
1088
|
+
try {
|
|
1089
|
+
const { payload, session } = this._preparePluginPayload({ code }, options.session);
|
|
1090
|
+
if (inviteToken) {
|
|
1091
|
+
payload.inviteToken = inviteToken;
|
|
1092
|
+
}
|
|
1093
|
+
const response = await this._request("/auth/github", {
|
|
1094
|
+
method: "POST",
|
|
1095
|
+
body: JSON.stringify(payload),
|
|
1096
|
+
methodName: "githubAuth"
|
|
1097
|
+
});
|
|
1098
|
+
if (response.success && response.data && response.data.tokens) {
|
|
1099
|
+
const { tokens } = response.data;
|
|
1100
|
+
const tokenData = {
|
|
1101
|
+
access_token: tokens.accessToken,
|
|
1102
|
+
refresh_token: tokens.refreshToken,
|
|
1103
|
+
expires_in: (_a = tokens.accessTokenExp) == null ? void 0 : _a.expiresIn,
|
|
1104
|
+
token_type: "Bearer"
|
|
1105
|
+
};
|
|
1106
|
+
if (this._tokenManager) {
|
|
1107
|
+
this._tokenManager.setTokens(tokenData);
|
|
1108
|
+
}
|
|
1109
|
+
}
|
|
1110
|
+
if (response.success) {
|
|
1111
|
+
if (session) {
|
|
1112
|
+
this._clearPluginSession(session);
|
|
1113
|
+
}
|
|
1114
|
+
return response.data;
|
|
1115
|
+
}
|
|
1116
|
+
throw new Error(response.message);
|
|
1117
|
+
} catch (error) {
|
|
1118
|
+
throw new Error(`GitHub auth failed: ${error.message}`, { cause: error });
|
|
311
1119
|
}
|
|
312
1120
|
}
|
|
313
|
-
|
|
1121
|
+
async googleAuthCallback(code, redirectUri, inviteToken = null, options = {}) {
|
|
314
1122
|
var _a;
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
1123
|
+
try {
|
|
1124
|
+
const { payload: body, session } = this._preparePluginPayload(
|
|
1125
|
+
{ code, redirectUri },
|
|
1126
|
+
options.session
|
|
1127
|
+
);
|
|
1128
|
+
if (inviteToken) {
|
|
1129
|
+
body.inviteToken = inviteToken;
|
|
1130
|
+
}
|
|
1131
|
+
const response = await this._request("/auth/google/callback", {
|
|
1132
|
+
method: "POST",
|
|
1133
|
+
body: JSON.stringify(body),
|
|
1134
|
+
methodName: "googleAuthCallback"
|
|
1135
|
+
});
|
|
1136
|
+
if (response.success && response.data && response.data.tokens) {
|
|
1137
|
+
const { tokens } = response.data;
|
|
1138
|
+
const tokenData = {
|
|
1139
|
+
access_token: tokens.accessToken,
|
|
1140
|
+
refresh_token: tokens.refreshToken,
|
|
1141
|
+
expires_in: (_a = tokens.accessTokenExp) == null ? void 0 : _a.expiresIn,
|
|
1142
|
+
token_type: "Bearer"
|
|
1143
|
+
};
|
|
1144
|
+
if (this._tokenManager) {
|
|
1145
|
+
this._tokenManager.setTokens(tokenData);
|
|
1146
|
+
}
|
|
1147
|
+
}
|
|
1148
|
+
if (response.success) {
|
|
1149
|
+
if (session) {
|
|
1150
|
+
this._clearPluginSession(session);
|
|
1151
|
+
}
|
|
1152
|
+
return response.data;
|
|
1153
|
+
}
|
|
1154
|
+
throw new Error(response.message);
|
|
1155
|
+
} catch (error) {
|
|
1156
|
+
throw new Error(`Google auth callback failed: ${error.message}`, { cause: error });
|
|
318
1157
|
}
|
|
319
|
-
return based._client;
|
|
320
1158
|
}
|
|
321
|
-
async
|
|
1159
|
+
async requestPasswordReset(email) {
|
|
322
1160
|
try {
|
|
323
|
-
const
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
}
|
|
328
|
-
based.setAuthState({
|
|
329
|
-
token: response.token,
|
|
330
|
-
userId: response.userId,
|
|
331
|
-
projectRoles: response.projectRoles,
|
|
332
|
-
globalRole: response.globalRole,
|
|
333
|
-
persistent: true
|
|
1161
|
+
const response = await this._request("/auth/request-password-reset", {
|
|
1162
|
+
method: "POST",
|
|
1163
|
+
body: JSON.stringify({ email }),
|
|
1164
|
+
methodName: "requestPasswordReset"
|
|
334
1165
|
});
|
|
335
|
-
|
|
1166
|
+
if (response.success) {
|
|
1167
|
+
return response.data;
|
|
1168
|
+
}
|
|
1169
|
+
throw new Error(response.message);
|
|
336
1170
|
} catch (error) {
|
|
337
|
-
throw new Error(`
|
|
1171
|
+
throw new Error(`Password reset request failed: ${error.message}`, { cause: error });
|
|
338
1172
|
}
|
|
339
1173
|
}
|
|
340
|
-
async
|
|
1174
|
+
async confirmPasswordReset(token, password) {
|
|
341
1175
|
try {
|
|
342
|
-
const
|
|
343
|
-
|
|
1176
|
+
const response = await this._request("/auth/reset-password-confirm", {
|
|
1177
|
+
method: "POST",
|
|
1178
|
+
body: JSON.stringify({ token, password }),
|
|
1179
|
+
methodName: "confirmPasswordReset"
|
|
1180
|
+
});
|
|
1181
|
+
if (response.success) {
|
|
1182
|
+
return response.data;
|
|
1183
|
+
}
|
|
1184
|
+
throw new Error(response.message);
|
|
344
1185
|
} catch (error) {
|
|
345
|
-
throw new Error(`
|
|
1186
|
+
throw new Error(`Password reset confirmation failed: ${error.message}`, { cause: error });
|
|
346
1187
|
}
|
|
347
1188
|
}
|
|
348
|
-
async
|
|
1189
|
+
async confirmRegistration(token) {
|
|
349
1190
|
try {
|
|
350
|
-
const
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
}
|
|
355
|
-
based.setAuthState({
|
|
356
|
-
token: response.token,
|
|
357
|
-
userId: response.userId,
|
|
358
|
-
persistent: true
|
|
1191
|
+
const response = await this._request("/auth/register-confirmation", {
|
|
1192
|
+
method: "POST",
|
|
1193
|
+
body: JSON.stringify({ token }),
|
|
1194
|
+
methodName: "confirmRegistration"
|
|
359
1195
|
});
|
|
360
|
-
|
|
1196
|
+
if (response.success) {
|
|
1197
|
+
return response.data;
|
|
1198
|
+
}
|
|
1199
|
+
throw new Error(response.message);
|
|
361
1200
|
} catch (error) {
|
|
362
|
-
throw new Error(`
|
|
1201
|
+
throw new Error(`Registration confirmation failed: ${error.message}`, { cause: error });
|
|
363
1202
|
}
|
|
364
1203
|
}
|
|
365
|
-
async
|
|
1204
|
+
async requestPasswordChange() {
|
|
1205
|
+
this._requireReady("requestPasswordChange");
|
|
366
1206
|
try {
|
|
367
|
-
const
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
redirectUri
|
|
1207
|
+
const response = await this._request("/auth/request-password-change", {
|
|
1208
|
+
method: "POST",
|
|
1209
|
+
methodName: "requestPasswordChange"
|
|
371
1210
|
});
|
|
372
|
-
if (
|
|
373
|
-
|
|
1211
|
+
if (response.success) {
|
|
1212
|
+
return response.data;
|
|
374
1213
|
}
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
1214
|
+
throw new Error(response.message);
|
|
1215
|
+
} catch (error) {
|
|
1216
|
+
throw new Error(`Password change request failed: ${error.message}`, { cause: error });
|
|
1217
|
+
}
|
|
1218
|
+
}
|
|
1219
|
+
async confirmPasswordChange(currentPassword, newPassword, code) {
|
|
1220
|
+
this._requireReady("confirmPasswordChange");
|
|
1221
|
+
try {
|
|
1222
|
+
const response = await this._request("/auth/confirm-password-change", {
|
|
1223
|
+
method: "POST",
|
|
1224
|
+
body: JSON.stringify({ currentPassword, newPassword, code }),
|
|
1225
|
+
methodName: "confirmPasswordChange"
|
|
379
1226
|
});
|
|
380
|
-
|
|
1227
|
+
if (response.success) {
|
|
1228
|
+
return response.data;
|
|
1229
|
+
}
|
|
1230
|
+
throw new Error(response.message);
|
|
381
1231
|
} catch (error) {
|
|
382
|
-
throw new Error(`
|
|
1232
|
+
throw new Error(`Password change confirmation failed: ${error.message}`, { cause: error });
|
|
383
1233
|
}
|
|
384
1234
|
}
|
|
385
|
-
async
|
|
1235
|
+
async getMe(options = {}) {
|
|
1236
|
+
this._requireReady("getMe");
|
|
386
1237
|
try {
|
|
387
|
-
const
|
|
388
|
-
const
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
based.setAuthState({
|
|
393
|
-
token: response.token,
|
|
394
|
-
userId: response.userId,
|
|
395
|
-
persistent: true
|
|
1238
|
+
const session = this._resolvePluginSession(options.session);
|
|
1239
|
+
const endpoint = session ? `/auth/me?session=${encodeURIComponent(session)}` : "/auth/me";
|
|
1240
|
+
const response = await this._request(endpoint, {
|
|
1241
|
+
method: "GET",
|
|
1242
|
+
methodName: "getMe"
|
|
396
1243
|
});
|
|
397
|
-
|
|
1244
|
+
if (response.success) {
|
|
1245
|
+
return response.data;
|
|
1246
|
+
}
|
|
1247
|
+
throw new Error(response.message);
|
|
398
1248
|
} catch (error) {
|
|
399
|
-
throw new Error(`
|
|
1249
|
+
throw new Error(`Failed to get user profile: ${error.message}`, { cause: error });
|
|
400
1250
|
}
|
|
401
1251
|
}
|
|
402
|
-
|
|
403
|
-
this.
|
|
1252
|
+
getAuthToken() {
|
|
1253
|
+
if (!this._tokenManager) {
|
|
1254
|
+
return null;
|
|
1255
|
+
}
|
|
1256
|
+
return this._tokenManager.getAccessToken();
|
|
1257
|
+
}
|
|
1258
|
+
/**
|
|
1259
|
+
* Get stored authentication state (backward compatibility method)
|
|
1260
|
+
* Replaces AuthService.getStoredAuthState()
|
|
1261
|
+
*/
|
|
1262
|
+
async getStoredAuthState() {
|
|
1263
|
+
try {
|
|
1264
|
+
if (!this._tokenManager) {
|
|
1265
|
+
return {
|
|
1266
|
+
userId: false,
|
|
1267
|
+
authToken: false
|
|
1268
|
+
};
|
|
1269
|
+
}
|
|
1270
|
+
const tokenStatus = this._tokenManager.getTokenStatus();
|
|
1271
|
+
if (!tokenStatus.hasTokens) {
|
|
1272
|
+
return {
|
|
1273
|
+
userId: false,
|
|
1274
|
+
authToken: false
|
|
1275
|
+
};
|
|
1276
|
+
}
|
|
1277
|
+
if (!tokenStatus.isValid && tokenStatus.hasRefreshToken) {
|
|
1278
|
+
try {
|
|
1279
|
+
await this._tokenManager.ensureValidToken();
|
|
1280
|
+
} catch (error) {
|
|
1281
|
+
console.warn("[AuthService] Token refresh failed:", error.message);
|
|
1282
|
+
if (error.message.includes("401") || error.message.includes("403") || error.message.includes("invalid") || error.message.includes("expired")) {
|
|
1283
|
+
this._tokenManager.clearTokens();
|
|
1284
|
+
return {
|
|
1285
|
+
userId: false,
|
|
1286
|
+
authToken: false,
|
|
1287
|
+
error: `Authentication failed: ${error.message}`
|
|
1288
|
+
};
|
|
1289
|
+
}
|
|
1290
|
+
return {
|
|
1291
|
+
userId: false,
|
|
1292
|
+
authToken: this._tokenManager.getAccessToken(),
|
|
1293
|
+
error: `Network error during token refresh: ${error.message}`,
|
|
1294
|
+
hasTokens: true
|
|
1295
|
+
};
|
|
1296
|
+
}
|
|
1297
|
+
}
|
|
1298
|
+
const currentAccessToken = this._tokenManager.getAccessToken();
|
|
1299
|
+
if (!currentAccessToken) {
|
|
1300
|
+
return {
|
|
1301
|
+
userId: false,
|
|
1302
|
+
authToken: false
|
|
1303
|
+
};
|
|
1304
|
+
}
|
|
1305
|
+
try {
|
|
1306
|
+
const currentUser = await this.getMe();
|
|
1307
|
+
return {
|
|
1308
|
+
userId: currentUser.user.id,
|
|
1309
|
+
authToken: currentAccessToken,
|
|
1310
|
+
...currentUser,
|
|
1311
|
+
error: null
|
|
1312
|
+
};
|
|
1313
|
+
} catch (error) {
|
|
1314
|
+
console.warn("[AuthService] Failed to get user data:", error.message);
|
|
1315
|
+
if (error.message.includes("401") || error.message.includes("403")) {
|
|
1316
|
+
this._tokenManager.clearTokens();
|
|
1317
|
+
return {
|
|
1318
|
+
userId: false,
|
|
1319
|
+
authToken: false,
|
|
1320
|
+
error: `Authentication failed: ${error.message}`
|
|
1321
|
+
};
|
|
1322
|
+
}
|
|
1323
|
+
return {
|
|
1324
|
+
userId: false,
|
|
1325
|
+
authToken: currentAccessToken,
|
|
1326
|
+
error: `Failed to get user data: ${error.message}`,
|
|
1327
|
+
hasTokens: true
|
|
1328
|
+
};
|
|
1329
|
+
}
|
|
1330
|
+
} catch (error) {
|
|
1331
|
+
console.error(
|
|
1332
|
+
"[AuthService] Unexpected error in getStoredAuthState:",
|
|
1333
|
+
error
|
|
1334
|
+
);
|
|
1335
|
+
return {
|
|
1336
|
+
userId: false,
|
|
1337
|
+
authToken: false,
|
|
1338
|
+
error: `Failed to get stored auth state: ${error.message}`
|
|
1339
|
+
};
|
|
1340
|
+
}
|
|
1341
|
+
}
|
|
1342
|
+
// ==================== USER METHODS ====================
|
|
1343
|
+
async getUserProfile() {
|
|
1344
|
+
this._requireReady("getUserProfile");
|
|
404
1345
|
try {
|
|
405
|
-
const
|
|
406
|
-
|
|
407
|
-
|
|
1346
|
+
const response = await this._request("/users/profile", {
|
|
1347
|
+
method: "GET",
|
|
1348
|
+
methodName: "getUserProfile"
|
|
1349
|
+
});
|
|
1350
|
+
if (response.success) {
|
|
1351
|
+
return response.data;
|
|
1352
|
+
}
|
|
1353
|
+
throw new Error(response.message);
|
|
408
1354
|
} catch (error) {
|
|
409
|
-
throw new Error(`
|
|
1355
|
+
throw new Error(`Failed to get user profile: ${error.message}`, { cause: error });
|
|
410
1356
|
}
|
|
411
1357
|
}
|
|
412
|
-
async
|
|
413
|
-
this._requireReady("
|
|
1358
|
+
async updateUserProfile(profileData) {
|
|
1359
|
+
this._requireReady("updateUserProfile");
|
|
1360
|
+
try {
|
|
1361
|
+
const response = await this._request("/users/profile", {
|
|
1362
|
+
method: "PATCH",
|
|
1363
|
+
body: JSON.stringify(profileData),
|
|
1364
|
+
methodName: "updateUserProfile"
|
|
1365
|
+
});
|
|
1366
|
+
if (response.success) {
|
|
1367
|
+
return response.data;
|
|
1368
|
+
}
|
|
1369
|
+
throw new Error(response.message);
|
|
1370
|
+
} catch (error) {
|
|
1371
|
+
throw new Error(`Failed to update user profile: ${error.message}`, { cause: error });
|
|
1372
|
+
}
|
|
1373
|
+
}
|
|
1374
|
+
async getUserProjects() {
|
|
1375
|
+
this._requireReady("getUserProjects");
|
|
1376
|
+
try {
|
|
1377
|
+
const response = await this._request("/users/projects", {
|
|
1378
|
+
method: "GET",
|
|
1379
|
+
methodName: "getUserProjects"
|
|
1380
|
+
});
|
|
1381
|
+
if (response.success) {
|
|
1382
|
+
return response.data.map((project) => ({
|
|
1383
|
+
...project,
|
|
1384
|
+
...project.icon && {
|
|
1385
|
+
icon: {
|
|
1386
|
+
src: `${this._apiUrl}/core/files/public/${project.icon.id}/download`,
|
|
1387
|
+
...project.icon
|
|
1388
|
+
}
|
|
1389
|
+
}
|
|
1390
|
+
}));
|
|
1391
|
+
}
|
|
1392
|
+
throw new Error(response.message);
|
|
1393
|
+
} catch (error) {
|
|
1394
|
+
throw new Error(`Failed to get user projects: ${error.message}`, { cause: error });
|
|
1395
|
+
}
|
|
1396
|
+
}
|
|
1397
|
+
async getUser(userId) {
|
|
1398
|
+
this._requireReady("getUser");
|
|
414
1399
|
if (!userId) {
|
|
415
1400
|
throw new Error("User ID is required");
|
|
416
1401
|
}
|
|
417
|
-
|
|
418
|
-
|
|
1402
|
+
try {
|
|
1403
|
+
const response = await this._request(`/users/${userId}`, {
|
|
1404
|
+
method: "GET",
|
|
1405
|
+
methodName: "getUser"
|
|
1406
|
+
});
|
|
1407
|
+
if (response.success) {
|
|
1408
|
+
return response.data;
|
|
1409
|
+
}
|
|
1410
|
+
throw new Error(response.message);
|
|
1411
|
+
} catch (error) {
|
|
1412
|
+
throw new Error(`Failed to get user: ${error.message}`, { cause: error });
|
|
1413
|
+
}
|
|
1414
|
+
}
|
|
1415
|
+
async getUserByEmail(email) {
|
|
1416
|
+
this._requireReady("getUserByEmail");
|
|
1417
|
+
if (!email) {
|
|
1418
|
+
throw new Error("Email is required");
|
|
419
1419
|
}
|
|
420
1420
|
try {
|
|
421
|
-
const
|
|
422
|
-
|
|
1421
|
+
const response = await this._request(`/auth/user?email=${email}`, {
|
|
1422
|
+
method: "GET",
|
|
1423
|
+
methodName: "getUserByEmail"
|
|
1424
|
+
});
|
|
1425
|
+
if (response.success) {
|
|
1426
|
+
return response.data.user;
|
|
1427
|
+
}
|
|
1428
|
+
throw new Error(response.message);
|
|
423
1429
|
} catch (error) {
|
|
424
|
-
throw new Error(`Failed to
|
|
1430
|
+
throw new Error(`Failed to get user by email: ${error.message}`, { cause: error });
|
|
425
1431
|
}
|
|
426
1432
|
}
|
|
427
|
-
|
|
428
|
-
|
|
1433
|
+
// ==================== PROJECT ROLE METHODS ====================
|
|
1434
|
+
/**
|
|
1435
|
+
* Get the current user's role for a specific project by project ID
|
|
1436
|
+
* Uses caching to avoid repeated API calls
|
|
1437
|
+
*/
|
|
1438
|
+
async getMyProjectRole(projectId) {
|
|
1439
|
+
this._requireReady("getMyProjectRole");
|
|
429
1440
|
if (!projectId) {
|
|
430
1441
|
throw new Error("Project ID is required");
|
|
431
1442
|
}
|
|
432
|
-
if (!this.
|
|
433
|
-
|
|
1443
|
+
if (!this.hasValidTokens()) {
|
|
1444
|
+
return "guest";
|
|
1445
|
+
}
|
|
1446
|
+
const cacheKey = `role_${projectId}`;
|
|
1447
|
+
const cached = this._projectRoleCache.get(cacheKey);
|
|
1448
|
+
if (cached && Date.now() - cached.timestamp < this._roleCacheExpiry) {
|
|
1449
|
+
return cached.role;
|
|
1450
|
+
}
|
|
1451
|
+
try {
|
|
1452
|
+
const response = await this._request(`/projects/${projectId}/role`, {
|
|
1453
|
+
method: "GET",
|
|
1454
|
+
methodName: "getMyProjectRole"
|
|
1455
|
+
});
|
|
1456
|
+
if (response.success) {
|
|
1457
|
+
const { role } = response.data;
|
|
1458
|
+
this._projectRoleCache.set(cacheKey, {
|
|
1459
|
+
role,
|
|
1460
|
+
timestamp: Date.now()
|
|
1461
|
+
});
|
|
1462
|
+
return role;
|
|
1463
|
+
}
|
|
1464
|
+
throw new Error(response.message);
|
|
1465
|
+
} catch (error) {
|
|
1466
|
+
const message = (error == null ? void 0 : error.message) || "";
|
|
1467
|
+
if (/401|403|unauthorized|no token|invalid token/iu.test(message)) {
|
|
1468
|
+
return "guest";
|
|
1469
|
+
}
|
|
1470
|
+
throw new Error(`Failed to get project role: ${message}`, { cause: error });
|
|
1471
|
+
}
|
|
1472
|
+
}
|
|
1473
|
+
/**
|
|
1474
|
+
* Get the current user's role for a specific project by project key
|
|
1475
|
+
* Uses caching to avoid repeated API calls
|
|
1476
|
+
*/
|
|
1477
|
+
async getMyProjectRoleByKey(projectKey) {
|
|
1478
|
+
this._requireReady("getMyProjectRoleByKey");
|
|
1479
|
+
if (!projectKey) {
|
|
1480
|
+
throw new Error("Project key is required");
|
|
1481
|
+
}
|
|
1482
|
+
if (!this.hasValidTokens()) {
|
|
1483
|
+
return "guest";
|
|
1484
|
+
}
|
|
1485
|
+
const cacheKey = `role_key_${projectKey}`;
|
|
1486
|
+
const cached = this._projectRoleCache.get(cacheKey);
|
|
1487
|
+
if (cached && Date.now() - cached.timestamp < this._roleCacheExpiry) {
|
|
1488
|
+
return cached.role;
|
|
434
1489
|
}
|
|
435
1490
|
try {
|
|
436
|
-
const
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
tier: newTier
|
|
1491
|
+
const response = await this._request(`/projects/key/${projectKey}/role`, {
|
|
1492
|
+
method: "GET",
|
|
1493
|
+
methodName: "getMyProjectRoleByKey"
|
|
440
1494
|
});
|
|
1495
|
+
if (response.success) {
|
|
1496
|
+
const { role } = response.data;
|
|
1497
|
+
this._projectRoleCache.set(cacheKey, {
|
|
1498
|
+
role,
|
|
1499
|
+
timestamp: Date.now()
|
|
1500
|
+
});
|
|
1501
|
+
return role;
|
|
1502
|
+
}
|
|
1503
|
+
throw new Error(response.message);
|
|
1504
|
+
} catch (error) {
|
|
1505
|
+
const message = (error == null ? void 0 : error.message) || "";
|
|
1506
|
+
if (/401|403|unauthorized|no token|invalid token/iu.test(message)) {
|
|
1507
|
+
return "guest";
|
|
1508
|
+
}
|
|
1509
|
+
throw new Error(`Failed to get project role by key: ${message}`, { cause: error });
|
|
1510
|
+
}
|
|
1511
|
+
}
|
|
1512
|
+
/**
|
|
1513
|
+
* Clear the project role cache for a specific project or all projects
|
|
1514
|
+
*/
|
|
1515
|
+
clearProjectRoleCache(projectId = null) {
|
|
1516
|
+
if (projectId) {
|
|
1517
|
+
this._projectRoleCache.delete(`role_${projectId}`);
|
|
1518
|
+
for (const [key] of this._projectRoleCache) {
|
|
1519
|
+
if (key.startsWith("role_key_")) {
|
|
1520
|
+
this._projectRoleCache.delete(key);
|
|
1521
|
+
}
|
|
1522
|
+
}
|
|
1523
|
+
} else {
|
|
1524
|
+
this._projectRoleCache.clear();
|
|
1525
|
+
}
|
|
1526
|
+
}
|
|
1527
|
+
/**
|
|
1528
|
+
* Get project role with fallback to user projects list
|
|
1529
|
+
* This method tries to get the role from user projects first,
|
|
1530
|
+
* then falls back to API call if not found
|
|
1531
|
+
*/
|
|
1532
|
+
async getProjectRoleWithFallback(projectId, userProjects = null) {
|
|
1533
|
+
this._requireReady("getProjectRoleWithFallback");
|
|
1534
|
+
if (!projectId) {
|
|
1535
|
+
throw new Error("Project ID is required");
|
|
1536
|
+
}
|
|
1537
|
+
if (userProjects && Array.isArray(userProjects)) {
|
|
1538
|
+
const userProject = userProjects.find((p) => p.id === projectId);
|
|
1539
|
+
if (userProject && userProject.role) {
|
|
1540
|
+
return userProject.role;
|
|
1541
|
+
}
|
|
1542
|
+
}
|
|
1543
|
+
return await this.getMyProjectRole(projectId);
|
|
1544
|
+
}
|
|
1545
|
+
/**
|
|
1546
|
+
* Get project role with fallback to user projects list (by project key)
|
|
1547
|
+
* This method tries to get the role from user projects first,
|
|
1548
|
+
* then falls back to API call if not found
|
|
1549
|
+
*/
|
|
1550
|
+
async getProjectRoleByKeyWithFallback(projectKey, userProjects = null) {
|
|
1551
|
+
this._requireReady("getProjectRoleByKeyWithFallback");
|
|
1552
|
+
if (!projectKey) {
|
|
1553
|
+
throw new Error("Project key is required");
|
|
1554
|
+
}
|
|
1555
|
+
if (userProjects && Array.isArray(userProjects)) {
|
|
1556
|
+
const userProject = userProjects.find((p) => p.key === projectKey);
|
|
1557
|
+
if (userProject && userProject.role) {
|
|
1558
|
+
return userProject.role;
|
|
1559
|
+
}
|
|
1560
|
+
}
|
|
1561
|
+
return await this.getMyProjectRoleByKey(projectKey);
|
|
1562
|
+
}
|
|
1563
|
+
// ==================== AUTH HELPER METHODS ====================
|
|
1564
|
+
/**
|
|
1565
|
+
* Debug method to check token status
|
|
1566
|
+
*/
|
|
1567
|
+
getTokenDebugInfo() {
|
|
1568
|
+
if (!this._tokenManager) {
|
|
1569
|
+
return {
|
|
1570
|
+
tokenManagerExists: false,
|
|
1571
|
+
error: "TokenManager not initialized"
|
|
1572
|
+
};
|
|
1573
|
+
}
|
|
1574
|
+
const tokenStatus = this._tokenManager.getTokenStatus();
|
|
1575
|
+
const { tokens } = this._tokenManager;
|
|
1576
|
+
return {
|
|
1577
|
+
tokenManagerExists: true,
|
|
1578
|
+
tokenStatus,
|
|
1579
|
+
hasAccessToken: Boolean(tokens.accessToken),
|
|
1580
|
+
hasRefreshToken: Boolean(tokens.refreshToken),
|
|
1581
|
+
accessTokenPreview: tokens.accessToken ? `${tokens.accessToken.substring(0, 20)}...` : null,
|
|
1582
|
+
expiresAt: tokens.expiresAt,
|
|
1583
|
+
timeToExpiry: tokenStatus.timeToExpiry,
|
|
1584
|
+
authHeader: this._tokenManager.getAuthHeader()
|
|
1585
|
+
};
|
|
1586
|
+
}
|
|
1587
|
+
/**
|
|
1588
|
+
* Helper method to check if user is authenticated
|
|
1589
|
+
*/
|
|
1590
|
+
isAuthenticated() {
|
|
1591
|
+
if (!this._tokenManager) {
|
|
1592
|
+
return false;
|
|
1593
|
+
}
|
|
1594
|
+
return this._tokenManager.hasTokens();
|
|
1595
|
+
}
|
|
1596
|
+
/**
|
|
1597
|
+
* Helper method to check if user has valid tokens
|
|
1598
|
+
*/
|
|
1599
|
+
hasValidTokens() {
|
|
1600
|
+
if (!this._tokenManager) {
|
|
1601
|
+
return false;
|
|
1602
|
+
}
|
|
1603
|
+
return this._tokenManager.hasTokens() && this._tokenManager.isAccessTokenValid();
|
|
1604
|
+
}
|
|
1605
|
+
/**
|
|
1606
|
+
* Helper method to get current user info
|
|
1607
|
+
*/
|
|
1608
|
+
async getCurrentUser() {
|
|
1609
|
+
try {
|
|
1610
|
+
return await this.getMe();
|
|
441
1611
|
} catch (error) {
|
|
442
|
-
throw new Error(`Failed to
|
|
1612
|
+
throw new Error(`Failed to get current user: ${error.message}`, { cause: error });
|
|
443
1613
|
}
|
|
444
1614
|
}
|
|
1615
|
+
/**
|
|
1616
|
+
* Helper method to validate user data for registration
|
|
1617
|
+
*/
|
|
1618
|
+
validateRegistrationData(userData) {
|
|
1619
|
+
const errors = [];
|
|
1620
|
+
if (!userData.email || typeof userData.email !== "string") {
|
|
1621
|
+
errors.push("Email is required and must be a string");
|
|
1622
|
+
} else if (!this._isValidEmail(userData.email)) {
|
|
1623
|
+
errors.push("Email must be a valid email address");
|
|
1624
|
+
}
|
|
1625
|
+
if (!userData.password || typeof userData.password !== "string") {
|
|
1626
|
+
errors.push("Password is required and must be a string");
|
|
1627
|
+
} else if (userData.password.length < 8) {
|
|
1628
|
+
errors.push("Password must be at least 8 characters long");
|
|
1629
|
+
}
|
|
1630
|
+
if (userData.username && typeof userData.username !== "string") {
|
|
1631
|
+
errors.push("Username must be a string");
|
|
1632
|
+
} else if (userData.username && userData.username.length < 3) {
|
|
1633
|
+
errors.push("Username must be at least 3 characters long");
|
|
1634
|
+
}
|
|
1635
|
+
return {
|
|
1636
|
+
isValid: errors.length === 0,
|
|
1637
|
+
errors
|
|
1638
|
+
};
|
|
1639
|
+
}
|
|
1640
|
+
/**
|
|
1641
|
+
* Helper method to register with validation
|
|
1642
|
+
*/
|
|
1643
|
+
async registerWithValidation(userData, options = {}) {
|
|
1644
|
+
const validation = this.validateRegistrationData(userData);
|
|
1645
|
+
if (!validation.isValid) {
|
|
1646
|
+
throw new Error(`Validation failed: ${validation.errors.join(", ")}`);
|
|
1647
|
+
}
|
|
1648
|
+
return await this.register(userData, options);
|
|
1649
|
+
}
|
|
1650
|
+
/**
|
|
1651
|
+
* Helper method to login with validation
|
|
1652
|
+
*/
|
|
1653
|
+
async loginWithValidation(email, password, options = {}) {
|
|
1654
|
+
if (!email || typeof email !== "string") {
|
|
1655
|
+
throw new Error("Email is required and must be a string");
|
|
1656
|
+
}
|
|
1657
|
+
if (!password || typeof password !== "string") {
|
|
1658
|
+
throw new Error("Password is required and must be a string");
|
|
1659
|
+
}
|
|
1660
|
+
if (!this._isValidEmail(email)) {
|
|
1661
|
+
throw new Error("Email must be a valid email address");
|
|
1662
|
+
}
|
|
1663
|
+
return await this.login(email, password, options);
|
|
1664
|
+
}
|
|
1665
|
+
/**
|
|
1666
|
+
* Private helper to validate email format
|
|
1667
|
+
*/
|
|
1668
|
+
_isValidEmail(email) {
|
|
1669
|
+
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/u;
|
|
1670
|
+
return emailRegex.test(email);
|
|
1671
|
+
}
|
|
1672
|
+
// ==================== PERMISSION METHODS (Existing) ====================
|
|
445
1673
|
hasPermission(requiredPermission) {
|
|
446
1674
|
var _a;
|
|
447
1675
|
const authState = (_a = this._context) == null ? void 0 : _a.state;
|
|
@@ -490,23 +1718,17 @@ var AuthService = class extends BaseService {
|
|
|
490
1718
|
if (!operationConfig) {
|
|
491
1719
|
return false;
|
|
492
1720
|
}
|
|
493
|
-
if (!operationConfig) {
|
|
494
|
-
return false;
|
|
495
|
-
}
|
|
496
1721
|
const { permissions = [], features = [] } = operationConfig;
|
|
497
1722
|
try {
|
|
498
1723
|
const permissionResults = await Promise.all(
|
|
499
1724
|
permissions.map(
|
|
500
|
-
(permission) => this.
|
|
1725
|
+
(permission) => this.checkProjectPermission(projectId, permission)
|
|
501
1726
|
)
|
|
502
1727
|
);
|
|
503
1728
|
const hasPermissions = requireAll ? permissionResults.every(Boolean) : permissionResults.some(Boolean);
|
|
504
1729
|
if (!hasPermissions) {
|
|
505
1730
|
return false;
|
|
506
1731
|
}
|
|
507
|
-
if (!hasPermissions) {
|
|
508
|
-
return false;
|
|
509
|
-
}
|
|
510
1732
|
if (checkFeatures && features.length > 0) {
|
|
511
1733
|
const featureResults = features.map((feature) => {
|
|
512
1734
|
const result = this.hasProjectFeature(projectId, feature);
|
|
@@ -516,9 +1738,6 @@ var AuthService = class extends BaseService {
|
|
|
516
1738
|
if (!hasFeatures) {
|
|
517
1739
|
return false;
|
|
518
1740
|
}
|
|
519
|
-
if (!hasFeatures) {
|
|
520
|
-
return false;
|
|
521
|
-
}
|
|
522
1741
|
}
|
|
523
1742
|
return true;
|
|
524
1743
|
} catch (error) {
|
|
@@ -540,206 +1759,94 @@ var AuthService = class extends BaseService {
|
|
|
540
1759
|
}
|
|
541
1760
|
return action();
|
|
542
1761
|
}
|
|
543
|
-
//
|
|
544
|
-
|
|
545
|
-
this.
|
|
546
|
-
|
|
547
|
-
|
|
1762
|
+
// Cleanup
|
|
1763
|
+
destroy() {
|
|
1764
|
+
if (this._tokenManager) {
|
|
1765
|
+
this._tokenManager.destroy();
|
|
1766
|
+
this._tokenManager = null;
|
|
548
1767
|
}
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
operations.map(async (operation) => {
|
|
552
|
-
const allowed = await this.canPerformOperation(projectId, operation);
|
|
553
|
-
const config = PERMISSION_MAP[operation];
|
|
554
|
-
return {
|
|
555
|
-
operation,
|
|
556
|
-
allowed,
|
|
557
|
-
permissions: config.permissions,
|
|
558
|
-
features: config.features,
|
|
559
|
-
aiTokens: operation.startsWith("ai") ? this._getAITokens(projectId, operation.replace("ai", "")) : null
|
|
560
|
-
};
|
|
561
|
-
})
|
|
562
|
-
);
|
|
563
|
-
return {
|
|
564
|
-
projectId,
|
|
565
|
-
permissions: access.reduce(
|
|
566
|
-
(acc, { operation, ...details }) => ({
|
|
567
|
-
...acc,
|
|
568
|
-
[operation]: details
|
|
569
|
-
}),
|
|
570
|
-
{}
|
|
571
|
-
),
|
|
572
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
573
|
-
};
|
|
1768
|
+
this._projectRoleCache.clear();
|
|
1769
|
+
this._setReady(false);
|
|
574
1770
|
}
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
const
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
];
|
|
582
|
-
return tokenFeatures.reduce((total, feature) => {
|
|
583
|
-
const tokens = this.hasProjectFeature(projectId, feature);
|
|
584
|
-
return total + (typeof tokens === "number" ? tokens : 0);
|
|
585
|
-
}, 0);
|
|
586
|
-
}
|
|
587
|
-
async getProjectMembers(projectId) {
|
|
588
|
-
var _a;
|
|
589
|
-
this._requireReady("getProjectMembers");
|
|
590
|
-
if (!projectId) {
|
|
591
|
-
throw new Error("Project ID is required");
|
|
592
|
-
}
|
|
593
|
-
try {
|
|
594
|
-
const based = this._getBasedService("getProjectMembers");
|
|
595
|
-
return await based.call("projects:get-members", { projectId });
|
|
596
|
-
} catch (error) {
|
|
597
|
-
if ((_a = error.message) == null ? void 0 : _a.includes("Authentication failed. Please try again"))
|
|
598
|
-
window.location.reload();
|
|
599
|
-
throw new Error(`Failed to get project members: ${error.message}`);
|
|
600
|
-
}
|
|
601
|
-
}
|
|
602
|
-
async inviteMember(projectId, email, role, name, callbackUrl) {
|
|
603
|
-
this._requireReady("inviteMember");
|
|
604
|
-
if (!projectId) {
|
|
605
|
-
throw new Error("Project ID is required");
|
|
606
|
-
}
|
|
607
|
-
if (!email) {
|
|
608
|
-
throw new Error("Email is required");
|
|
609
|
-
}
|
|
610
|
-
if (!callbackUrl || Object.keys(callbackUrl).length === 0) {
|
|
611
|
-
throw new Error("Callback Url is required");
|
|
612
|
-
}
|
|
613
|
-
if (!role || !this._userRoles.has(role)) {
|
|
614
|
-
throw new Error(`Invalid role: ${role}`);
|
|
615
|
-
}
|
|
616
|
-
try {
|
|
617
|
-
const based = this._getBasedService("inviteMember");
|
|
618
|
-
return await based.call("projects:invite-member", {
|
|
619
|
-
projectId,
|
|
620
|
-
email,
|
|
621
|
-
role,
|
|
622
|
-
name,
|
|
623
|
-
callbackUrl
|
|
624
|
-
});
|
|
625
|
-
} catch (error) {
|
|
626
|
-
throw new Error(`Failed to invite member: ${error.message}`);
|
|
1771
|
+
_preparePluginPayload(payload, sessionOverride = null) {
|
|
1772
|
+
const target = payload && typeof payload === "object" ? { ...payload } : {};
|
|
1773
|
+
const session = this._resolvePluginSession(sessionOverride);
|
|
1774
|
+
if (session && !Object.hasOwn(target, "session")) {
|
|
1775
|
+
target.session = session;
|
|
1776
|
+
return { payload: target, session };
|
|
627
1777
|
}
|
|
1778
|
+
return { payload: target, session: null };
|
|
628
1779
|
}
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
return await based.call("projects:accept-invite", { token });
|
|
634
|
-
} catch (error) {
|
|
635
|
-
throw new Error(`Failed to accept invite: ${error.message}`);
|
|
1780
|
+
_resolvePluginSession(sessionOverride = null) {
|
|
1781
|
+
var _a, _b;
|
|
1782
|
+
if (sessionOverride) {
|
|
1783
|
+
return this._cachePluginSession(sessionOverride);
|
|
636
1784
|
}
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
this._requireReady("updateMemberRole");
|
|
640
|
-
if (!projectId) {
|
|
641
|
-
throw new Error("Project ID is required");
|
|
1785
|
+
if (this._pluginSession) {
|
|
1786
|
+
return this._pluginSession;
|
|
642
1787
|
}
|
|
643
|
-
|
|
644
|
-
|
|
1788
|
+
const optionSession = (_a = this._options) == null ? void 0 : _a.pluginSession;
|
|
1789
|
+
if (optionSession) {
|
|
1790
|
+
return this._cachePluginSession(optionSession);
|
|
645
1791
|
}
|
|
646
|
-
|
|
647
|
-
|
|
1792
|
+
const contextSession = (_b = this._context) == null ? void 0 : _b.pluginSession;
|
|
1793
|
+
if (contextSession) {
|
|
1794
|
+
return this._cachePluginSession(contextSession);
|
|
648
1795
|
}
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
}
|
|
656
|
-
|
|
657
|
-
|
|
1796
|
+
if (typeof window !== "undefined") {
|
|
1797
|
+
try {
|
|
1798
|
+
const sessionFromUrl = new URL(window.location.href).searchParams.get("session");
|
|
1799
|
+
if (sessionFromUrl) {
|
|
1800
|
+
return this._cachePluginSession(sessionFromUrl);
|
|
1801
|
+
}
|
|
1802
|
+
} catch {
|
|
1803
|
+
}
|
|
1804
|
+
try {
|
|
1805
|
+
if (window.localStorage) {
|
|
1806
|
+
const stored = window.localStorage.getItem(PLUGIN_SESSION_STORAGE_KEY);
|
|
1807
|
+
if (stored) {
|
|
1808
|
+
this._pluginSession = stored;
|
|
1809
|
+
return stored;
|
|
1810
|
+
}
|
|
1811
|
+
}
|
|
1812
|
+
} catch {
|
|
1813
|
+
}
|
|
658
1814
|
}
|
|
1815
|
+
return null;
|
|
659
1816
|
}
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
throw new Error("Project ID and user ID are required");
|
|
1817
|
+
_cachePluginSession(session) {
|
|
1818
|
+
if (!session) {
|
|
1819
|
+
return null;
|
|
664
1820
|
}
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
try {
|
|
674
|
-
const based = this._getBasedService("confirmRegistration");
|
|
675
|
-
return await based.call("users:register-confirmation", { token });
|
|
676
|
-
} catch (error) {
|
|
677
|
-
throw new Error(`Registration confirmation failed: ${error.message}`);
|
|
1821
|
+
this._pluginSession = session;
|
|
1822
|
+
if (typeof window !== "undefined") {
|
|
1823
|
+
try {
|
|
1824
|
+
if (window.localStorage) {
|
|
1825
|
+
window.localStorage.setItem(PLUGIN_SESSION_STORAGE_KEY, session);
|
|
1826
|
+
}
|
|
1827
|
+
} catch {
|
|
1828
|
+
}
|
|
678
1829
|
}
|
|
1830
|
+
return session;
|
|
679
1831
|
}
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
const based = this._getBasedService("requestPasswordReset");
|
|
683
|
-
return await based.call("users:reset-password", { email, callbackUrl });
|
|
684
|
-
} catch (error) {
|
|
685
|
-
throw new Error(`Password reset request failed: ${error.message}`);
|
|
686
|
-
}
|
|
1832
|
+
setPluginSession(session) {
|
|
1833
|
+
this._cachePluginSession(session);
|
|
687
1834
|
}
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
return await based.call("users:reset-password-confirm", {
|
|
692
|
-
token,
|
|
693
|
-
newPassword
|
|
694
|
-
});
|
|
695
|
-
} catch (error) {
|
|
696
|
-
throw new Error(`Password reset confirmation failed: ${error.message}`);
|
|
1835
|
+
_clearPluginSession(session = null) {
|
|
1836
|
+
if (!session || this._pluginSession === session) {
|
|
1837
|
+
this._pluginSession = null;
|
|
697
1838
|
}
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
return {
|
|
705
|
-
userId: authState.userId,
|
|
706
|
-
authToken: authState.token,
|
|
707
|
-
projectRoles: authState.projectRoles,
|
|
708
|
-
globalRole: authState.globalRole,
|
|
709
|
-
error: null
|
|
710
|
-
};
|
|
1839
|
+
if (typeof window !== "undefined") {
|
|
1840
|
+
try {
|
|
1841
|
+
if (window.localStorage) {
|
|
1842
|
+
window.localStorage.removeItem(PLUGIN_SESSION_STORAGE_KEY);
|
|
1843
|
+
}
|
|
1844
|
+
} catch {
|
|
711
1845
|
}
|
|
712
|
-
return {
|
|
713
|
-
userId: false,
|
|
714
|
-
authToken: false
|
|
715
|
-
};
|
|
716
|
-
} catch (error) {
|
|
717
|
-
this._setError(error);
|
|
718
|
-
return {
|
|
719
|
-
userId: false,
|
|
720
|
-
authToken: false,
|
|
721
|
-
error: `Failed to get stored auth state: ${error.message}`
|
|
722
|
-
};
|
|
723
1846
|
}
|
|
724
1847
|
}
|
|
725
|
-
async subscribeToAuthChanges(callback) {
|
|
726
|
-
const based = this._getBasedService("subscribeToAuthChanges");
|
|
727
|
-
based.on("authstate-change", async (authState) => {
|
|
728
|
-
const formattedState = (authState == null ? void 0 : authState.token) ? {
|
|
729
|
-
userId: authState.userId,
|
|
730
|
-
authToken: authState.token,
|
|
731
|
-
projectRoles: authState.projectRoles,
|
|
732
|
-
globalRole: authState.globalRole,
|
|
733
|
-
error: null
|
|
734
|
-
} : {
|
|
735
|
-
userId: false,
|
|
736
|
-
authToken: false
|
|
737
|
-
};
|
|
738
|
-
await callback(formattedState);
|
|
739
|
-
});
|
|
740
|
-
return () => based.off("authstate-change");
|
|
741
|
-
}
|
|
742
1848
|
};
|
|
743
1849
|
export {
|
|
744
1850
|
AuthService
|
|
745
1851
|
};
|
|
1852
|
+
// @preserve-env
|