ultimate-jekyll-manager 0.0.200 → 0.0.201
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.
|
@@ -9,7 +9,7 @@ import authorizedFetch from '__main_assets__/js/libs/authorized-fetch.js';
|
|
|
9
9
|
let webManager = null;
|
|
10
10
|
let appData = null;
|
|
11
11
|
let accountData = null;
|
|
12
|
-
let connectionForms = new Map();
|
|
12
|
+
let connectionForms = new Map();
|
|
13
13
|
|
|
14
14
|
// Supported providers (must match IDs in HTML)
|
|
15
15
|
const supportedProviders = ['google', 'discord', 'github', 'twitter', 'facebook'];
|
|
@@ -38,79 +38,49 @@ export async function loadData(account, sharedAppData) {
|
|
|
38
38
|
|
|
39
39
|
// Display available and connected OAuth providers
|
|
40
40
|
function displayConnections() {
|
|
41
|
-
console.log('[DEBUG] displayConnections() called');
|
|
42
|
-
|
|
43
|
-
// Hide loading state
|
|
44
41
|
const $loading = document.getElementById('connections-loading');
|
|
42
|
+
|
|
45
43
|
if ($loading) {
|
|
46
44
|
$loading.classList.add('d-none');
|
|
47
45
|
}
|
|
48
46
|
|
|
49
|
-
// Check if appData is loaded
|
|
50
47
|
if (!appData) {
|
|
51
|
-
console.log('[DEBUG] No appData, showing loading state');
|
|
52
|
-
// Show loading if no app data yet
|
|
53
48
|
if ($loading) {
|
|
54
49
|
$loading.classList.remove('d-none');
|
|
55
50
|
}
|
|
56
51
|
return;
|
|
57
52
|
}
|
|
58
53
|
|
|
59
|
-
// Get available OAuth providers from app data
|
|
60
54
|
const availableProviders = appData?.oauth2 || {};
|
|
61
55
|
const userConnections = accountData?.oauth2 || {};
|
|
62
56
|
|
|
63
|
-
console.log('[DEBUG] Available OAuth providers:', availableProviders);
|
|
64
|
-
console.log('[DEBUG] User connections:', userConnections);
|
|
65
|
-
|
|
66
|
-
// Check if any providers are configured
|
|
67
57
|
let hasEnabledProviders = false;
|
|
68
58
|
|
|
69
|
-
// Process each supported provider
|
|
70
59
|
supportedProviders.forEach(providerId => {
|
|
71
|
-
console.log(`[DEBUG] Processing provider: ${providerId}`);
|
|
72
|
-
|
|
73
60
|
const providerSettings = availableProviders[providerId];
|
|
74
61
|
const $providerElement = document.getElementById(`connection-${providerId}`);
|
|
75
62
|
|
|
76
|
-
console.log(`[DEBUG] ${providerId} - providerSettings:`, providerSettings);
|
|
77
|
-
console.log(`[DEBUG] ${providerId} - providerSettings.enabled:`, providerSettings?.enabled);
|
|
78
|
-
console.log(`[DEBUG] ${providerId} - $providerElement exists:`, !!$providerElement);
|
|
79
|
-
|
|
80
63
|
if (!$providerElement) {
|
|
81
|
-
console.warn(`[DEBUG] ${providerId} - Provider element not found in DOM`);
|
|
82
64
|
return;
|
|
83
65
|
}
|
|
84
66
|
|
|
85
|
-
// Check if provider is enabled
|
|
86
67
|
const isEnabled = providerSettings && providerSettings.enabled !== false;
|
|
87
|
-
console.log(`[DEBUG] ${providerId} - isEnabled:`, isEnabled);
|
|
88
68
|
|
|
89
69
|
if (isEnabled) {
|
|
90
70
|
hasEnabledProviders = true;
|
|
91
|
-
|
|
92
|
-
// Show the provider element first
|
|
93
|
-
console.log(`[DEBUG] ${providerId} - Removing d-none class`);
|
|
94
71
|
$providerElement.classList.remove('d-none');
|
|
95
72
|
|
|
96
|
-
// Initialize FormManager for this provider before updating status
|
|
97
|
-
console.log(`[DEBUG] ${providerId} - About to initialize FormManager`);
|
|
98
73
|
initializeProviderForm(providerId);
|
|
99
74
|
|
|
100
|
-
// Update provider status based on user connection
|
|
101
75
|
const userConnection = userConnections[providerId];
|
|
102
|
-
console.log(`[DEBUG] ${providerId} - userConnection:`, userConnection);
|
|
103
|
-
console.log(`[DEBUG] ${providerId} - About to update provider status`);
|
|
104
76
|
updateProviderStatus(providerId, userConnection, providerSettings);
|
|
105
77
|
} else {
|
|
106
|
-
// Hide disabled providers
|
|
107
|
-
console.log(`[DEBUG] ${providerId} - Provider disabled, adding d-none class`);
|
|
108
78
|
$providerElement.classList.add('d-none');
|
|
109
79
|
}
|
|
110
80
|
});
|
|
111
81
|
|
|
112
|
-
// Show empty message if no providers are enabled
|
|
113
82
|
const $empty = document.getElementById('connections-empty');
|
|
83
|
+
|
|
114
84
|
if ($empty) {
|
|
115
85
|
if (hasEnabledProviders) {
|
|
116
86
|
$empty.classList.add('d-none');
|
|
@@ -122,30 +92,15 @@ function displayConnections() {
|
|
|
122
92
|
|
|
123
93
|
// Update provider status display
|
|
124
94
|
function updateProviderStatus(providerId, userConnection, providerSettings) {
|
|
125
|
-
console.log(`[DEBUG] updateProviderStatus() called for ${providerId}`);
|
|
126
|
-
console.log(`[DEBUG] ${providerId} - userConnection:`, userConnection);
|
|
127
|
-
console.log(`[DEBUG] ${providerId} - userConnection truthy:`, !!userConnection);
|
|
128
|
-
console.log(`[DEBUG] ${providerId} - userConnection.identity:`, userConnection?.identity);
|
|
129
|
-
console.log(`[DEBUG] ${providerId} - providerSettings:`, providerSettings);
|
|
130
|
-
|
|
131
95
|
const $status = document.getElementById(`${providerId}-connection-status`);
|
|
132
96
|
const $description = document.getElementById(`${providerId}-connection-description`);
|
|
133
97
|
const $form = document.getElementById(`connection-form-${providerId}`);
|
|
134
98
|
const $connectButton = $form?.querySelector('button[data-action="connect"]');
|
|
135
99
|
const $disconnectButton = $form?.querySelector('button[data-action="disconnect"]');
|
|
136
100
|
|
|
137
|
-
console.log(`[DEBUG] ${providerId} - DOM elements found:`, {
|
|
138
|
-
$status: !!$status,
|
|
139
|
-
$description: !!$description,
|
|
140
|
-
$form: !!$form,
|
|
141
|
-
$connectButton: !!$connectButton,
|
|
142
|
-
$disconnectButton: !!$disconnectButton,
|
|
143
|
-
});
|
|
144
|
-
|
|
145
101
|
const isConnected = userConnection && userConnection.identity;
|
|
146
|
-
console.log(`[DEBUG] ${providerId} - isConnected:`, isConnected);
|
|
147
102
|
|
|
148
|
-
//
|
|
103
|
+
// Set description
|
|
149
104
|
if ($description) {
|
|
150
105
|
const defaultDescriptions = {
|
|
151
106
|
google: 'Enable single sign-on with your Google account',
|
|
@@ -155,19 +110,20 @@ function updateProviderStatus(providerId, userConnection, providerSettings) {
|
|
|
155
110
|
facebook: 'Connect your Facebook account for social features',
|
|
156
111
|
};
|
|
157
112
|
|
|
158
|
-
|
|
159
|
-
|
|
113
|
+
const descriptionText = providerSettings?.description
|
|
114
|
+
|| defaultDescriptions[providerId]
|
|
115
|
+
|| `Connect your ${providerId.charAt(0).toUpperCase() + providerId.slice(1)} account`;
|
|
116
|
+
|
|
160
117
|
$description.textContent = descriptionText;
|
|
161
118
|
$description.classList.remove('d-none');
|
|
162
119
|
}
|
|
163
120
|
|
|
164
|
-
//
|
|
121
|
+
// Set status
|
|
165
122
|
if ($status) {
|
|
166
123
|
if (isConnected) {
|
|
167
124
|
const displayName = getConnectionDisplayName(userConnection);
|
|
168
125
|
let statusText = `Connected: ${displayName}`;
|
|
169
126
|
|
|
170
|
-
// Add last updated date if available
|
|
171
127
|
if (userConnection.updated && userConnection.updated.timestamp) {
|
|
172
128
|
const date = new Date(userConnection.updated.timestamp);
|
|
173
129
|
const dateStr = date.toLocaleDateString('en-US', {
|
|
@@ -181,26 +137,19 @@ function updateProviderStatus(providerId, userConnection, providerSettings) {
|
|
|
181
137
|
$status.textContent = statusText;
|
|
182
138
|
$status.classList.remove('d-none');
|
|
183
139
|
} else {
|
|
184
|
-
// Not connected - hide status text
|
|
185
140
|
$status.textContent = '';
|
|
186
141
|
$status.classList.add('d-none');
|
|
187
142
|
}
|
|
188
143
|
}
|
|
189
144
|
|
|
190
|
-
//
|
|
145
|
+
// Toggle buttons
|
|
191
146
|
if ($connectButton && $disconnectButton) {
|
|
192
|
-
console.log(`[DEBUG] ${providerId} - Updating buttons. isConnected:`, isConnected);
|
|
193
|
-
|
|
194
147
|
if (isConnected) {
|
|
195
|
-
// Hide connect button, show disconnect button
|
|
196
148
|
$connectButton.classList.add('d-none');
|
|
197
149
|
$disconnectButton.classList.remove('d-none');
|
|
198
|
-
console.log(`[DEBUG] ${providerId} - Showing disconnect button`);
|
|
199
150
|
} else {
|
|
200
|
-
// Show connect button, hide disconnect button
|
|
201
151
|
$connectButton.classList.remove('d-none');
|
|
202
152
|
$disconnectButton.classList.add('d-none');
|
|
203
|
-
console.log(`[DEBUG] ${providerId} - Showing connect button`);
|
|
204
153
|
}
|
|
205
154
|
}
|
|
206
155
|
}
|
|
@@ -211,7 +160,6 @@ function getConnectionDisplayName(connection) {
|
|
|
211
160
|
return 'Unknown';
|
|
212
161
|
}
|
|
213
162
|
|
|
214
|
-
// Try different fields based on provider
|
|
215
163
|
return connection.identity.global_name
|
|
216
164
|
|| connection.identity.username
|
|
217
165
|
|| connection.identity.name
|
|
@@ -223,35 +171,24 @@ function getConnectionDisplayName(connection) {
|
|
|
223
171
|
// Initialize FormManager for a provider
|
|
224
172
|
function initializeProviderForm(providerId) {
|
|
225
173
|
const formId = `connection-form-${providerId}`;
|
|
226
|
-
const form = document.getElementById(formId);
|
|
174
|
+
const $form = document.getElementById(formId);
|
|
227
175
|
|
|
228
|
-
if (
|
|
229
|
-
console.warn(`Form not found for provider: ${providerId}`);
|
|
176
|
+
if (!$form) {
|
|
230
177
|
return;
|
|
231
178
|
}
|
|
232
179
|
|
|
233
|
-
// Skip if already initialized
|
|
234
180
|
if (connectionForms.has(providerId)) {
|
|
235
|
-
console.log(`FormManager already initialized for ${providerId}`);
|
|
236
181
|
return;
|
|
237
182
|
}
|
|
238
183
|
|
|
239
|
-
console.log(`Initializing FormManager for ${providerId}`);
|
|
240
|
-
|
|
241
184
|
const formManager = new FormManager(`#${formId}`, {
|
|
242
185
|
submittingText: 'Connecting...',
|
|
243
186
|
});
|
|
244
187
|
|
|
245
|
-
// Store the FormManager instance
|
|
246
188
|
connectionForms.set(providerId, formManager);
|
|
247
189
|
|
|
248
|
-
// Listen for state changes to update button after FormManager is ready
|
|
249
190
|
formManager.on('statechange', ({ state }) => {
|
|
250
|
-
console.log(`[DEBUG] ${providerId} - FormManager state changed to:`, state);
|
|
251
|
-
|
|
252
|
-
// When FormManager transitions to ready, update the button status
|
|
253
191
|
if (state === 'ready') {
|
|
254
|
-
console.log(`[DEBUG] ${providerId} - FormManager is ready, updating button status`);
|
|
255
192
|
const userConnection = accountData?.oauth2?.[providerId];
|
|
256
193
|
const providerSettings = appData?.oauth2?.[providerId];
|
|
257
194
|
updateProviderStatus(providerId, userConnection, providerSettings);
|
|
@@ -260,18 +197,14 @@ function initializeProviderForm(providerId) {
|
|
|
260
197
|
|
|
261
198
|
formManager.on('submit', async ({ data, $submitButton }) => {
|
|
262
199
|
const provider = data.provider;
|
|
263
|
-
|
|
264
|
-
// Determine action from the clicked button's data-action attribute
|
|
265
200
|
const action = $submitButton?.getAttribute('data-action');
|
|
266
201
|
|
|
267
|
-
console.log(`[DEBUG] ${providerId} - Form submitted. Action:`, action, 'Provider:', provider);
|
|
268
|
-
|
|
269
202
|
if (action === 'connect') {
|
|
270
203
|
await handleConnect(provider);
|
|
271
204
|
} else if (action === 'disconnect') {
|
|
272
205
|
const success = await handleDisconnect(provider);
|
|
206
|
+
|
|
273
207
|
if (success) {
|
|
274
|
-
// Get provider settings to pass for description display
|
|
275
208
|
const providerSettings = appData?.oauth2?.[provider];
|
|
276
209
|
updateProviderStatus(provider, null, providerSettings);
|
|
277
210
|
}
|
|
@@ -282,30 +215,25 @@ function initializeProviderForm(providerId) {
|
|
|
282
215
|
// Handle connect action
|
|
283
216
|
async function handleConnect(providerId) {
|
|
284
217
|
const provider = appData?.oauth2?.[providerId];
|
|
218
|
+
|
|
285
219
|
if (!provider || provider.enabled === false) {
|
|
286
220
|
throw new Error('This connection service is not available.');
|
|
287
221
|
}
|
|
288
222
|
|
|
289
|
-
//
|
|
290
|
-
|
|
223
|
+
// Build URL with query params for GET request
|
|
224
|
+
// Don't send scope - let the backend use the provider's default scopes
|
|
225
|
+
const url = new URL(getApiUrl());
|
|
226
|
+
url.searchParams.set('provider', providerId);
|
|
227
|
+
url.searchParams.set('action', 'authorize');
|
|
228
|
+
url.searchParams.set('redirect', 'false');
|
|
291
229
|
|
|
292
|
-
const response = await authorizedFetch(
|
|
293
|
-
method: '
|
|
230
|
+
const response = await authorizedFetch(url.toString(), {
|
|
231
|
+
method: 'GET',
|
|
294
232
|
timeout: 30000,
|
|
295
233
|
response: 'json',
|
|
296
234
|
tries: 2,
|
|
297
|
-
body: {
|
|
298
|
-
redirect: false,
|
|
299
|
-
provider: providerId,
|
|
300
|
-
state: 'authorize',
|
|
301
|
-
scope: scope,
|
|
302
|
-
referrer: window.location.href,
|
|
303
|
-
},
|
|
304
235
|
});
|
|
305
236
|
|
|
306
|
-
console.log('OAuth connect response:', response);
|
|
307
|
-
|
|
308
|
-
// For authorize requests, server returns an object with URL to redirect to
|
|
309
237
|
if (response.url) {
|
|
310
238
|
window.location.href = response.url;
|
|
311
239
|
} else {
|
|
@@ -317,50 +245,36 @@ async function handleConnect(providerId) {
|
|
|
317
245
|
async function handleDisconnect(providerId) {
|
|
318
246
|
const providerName = providerId.charAt(0).toUpperCase() + providerId.slice(1);
|
|
319
247
|
|
|
320
|
-
// Wait 1 ms
|
|
321
248
|
await new Promise(resolve => setTimeout(resolve, 1));
|
|
322
249
|
|
|
323
|
-
// Confirm disconnection
|
|
324
250
|
if (!confirm(`Are you sure you want to disconnect your ${providerName} account?`)) {
|
|
325
251
|
throw new Error('Disconnection cancelled');
|
|
326
252
|
}
|
|
327
253
|
|
|
328
|
-
//
|
|
329
|
-
const
|
|
330
|
-
|
|
254
|
+
// Build URL with query params for DELETE request
|
|
255
|
+
const url = new URL(getApiUrl());
|
|
256
|
+
url.searchParams.set('provider', providerId);
|
|
331
257
|
|
|
332
|
-
const response = await authorizedFetch(
|
|
333
|
-
method: '
|
|
258
|
+
const response = await authorizedFetch(url.toString(), {
|
|
259
|
+
method: 'DELETE',
|
|
334
260
|
timeout: 30000,
|
|
335
261
|
response: 'json',
|
|
336
262
|
tries: 2,
|
|
337
|
-
body: {
|
|
338
|
-
redirect: false,
|
|
339
|
-
provider: providerId,
|
|
340
|
-
state: 'deauthorize',
|
|
341
|
-
scope: scope,
|
|
342
|
-
referrer: window.location.href,
|
|
343
|
-
},
|
|
344
263
|
});
|
|
345
264
|
|
|
346
|
-
console.log('OAuth disconnect response:', response);
|
|
347
|
-
|
|
348
265
|
if (response.success) {
|
|
349
|
-
// Update local account data
|
|
350
266
|
if (accountData.oauth2 && accountData.oauth2[providerId]) {
|
|
351
267
|
delete accountData.oauth2[providerId];
|
|
352
268
|
}
|
|
353
269
|
|
|
354
|
-
// Return success
|
|
355
270
|
return true;
|
|
356
|
-
} else {
|
|
357
|
-
throw new Error(response.message || 'Failed to disconnect');
|
|
358
271
|
}
|
|
272
|
+
|
|
273
|
+
throw new Error(response.message || 'Failed to disconnect');
|
|
359
274
|
}
|
|
360
275
|
|
|
361
276
|
// Called when section is shown
|
|
362
277
|
export function onShow() {
|
|
363
|
-
// Refresh connections display when section is shown
|
|
364
278
|
if (accountData && appData) {
|
|
365
279
|
displayConnections();
|
|
366
280
|
}
|
|
@@ -1,125 +1,74 @@
|
|
|
1
1
|
// Libraries
|
|
2
2
|
import authorizedFetch from '__main_assets__/js/libs/authorized-fetch.js';
|
|
3
|
+
|
|
3
4
|
let webManager = null;
|
|
4
5
|
|
|
5
6
|
// Module
|
|
6
|
-
export default (Manager
|
|
7
|
+
export default (Manager) => {
|
|
7
8
|
return new Promise(async function (resolve) {
|
|
8
|
-
// Set webManager
|
|
9
9
|
webManager = Manager.webManager;
|
|
10
10
|
|
|
11
|
-
// Initialize when DOM is ready
|
|
12
11
|
await webManager.dom().ready();
|
|
13
12
|
|
|
14
|
-
// Wait for auth state
|
|
15
|
-
//
|
|
13
|
+
// Wait for auth state before handling callback
|
|
14
|
+
// Required because authorizedFetch needs auth.currentUser
|
|
16
15
|
webManager.auth().listen({ once: true }, () => {
|
|
17
|
-
// Handle OAuth callback after auth state is known
|
|
18
16
|
handleOAuthCallback();
|
|
19
17
|
});
|
|
20
18
|
|
|
21
|
-
// Resolve after initialization
|
|
22
19
|
return resolve();
|
|
23
20
|
});
|
|
24
21
|
};
|
|
25
22
|
|
|
26
|
-
//
|
|
23
|
+
// Handle OAuth callback
|
|
27
24
|
async function handleOAuthCallback() {
|
|
28
|
-
const $loading = document.getElementById('oauth2-loading');
|
|
29
|
-
const $result = document.getElementById('oauth2-result');
|
|
30
25
|
const $provider = document.getElementById('oauth2-provider');
|
|
31
|
-
const $errorMessage = document.getElementById('error-message');
|
|
32
|
-
const $returnButton = document.getElementById('return-button');
|
|
33
|
-
const $resultSuccess = document.getElementById('result-success');
|
|
34
|
-
const $resultError = document.getElementById('result-error');
|
|
35
26
|
|
|
36
27
|
try {
|
|
37
|
-
// Get URL parameters
|
|
38
28
|
const urlParams = new URLSearchParams(window.location.search);
|
|
39
29
|
const code = urlParams.get('code');
|
|
40
|
-
const
|
|
30
|
+
const encryptedState = urlParams.get('state');
|
|
41
31
|
const error = urlParams.get('error');
|
|
42
32
|
const errorDescription = urlParams.get('error_description');
|
|
43
33
|
|
|
44
|
-
// Check for OAuth errors
|
|
34
|
+
// Check for OAuth errors from provider
|
|
45
35
|
if (error) {
|
|
46
36
|
throw new Error(errorDescription || error || 'OAuth authorization was denied');
|
|
47
37
|
}
|
|
48
38
|
|
|
49
|
-
// Validate required parameters
|
|
50
39
|
if (!code) {
|
|
51
40
|
throw new Error('Missing authorization code');
|
|
52
41
|
}
|
|
53
42
|
|
|
54
|
-
if (!
|
|
43
|
+
if (!encryptedState) {
|
|
55
44
|
throw new Error('Missing state parameter');
|
|
56
45
|
}
|
|
57
46
|
|
|
58
|
-
//
|
|
59
|
-
|
|
60
|
-
try {
|
|
61
|
-
stateParsed = JSON.parse(decodeURIComponent(state));
|
|
62
|
-
} catch (e) {
|
|
63
|
-
console.error('Failed to parse state:', e);
|
|
64
|
-
throw new Error('Invalid state parameter');
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
console.log('OAuth callback state:', stateParsed);
|
|
68
|
-
|
|
69
|
-
// Validate state
|
|
70
|
-
if (!stateParsed.provider) {
|
|
71
|
-
throw new Error('Missing provider in state');
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
if (!stateParsed.serverUrl) {
|
|
75
|
-
throw new Error('Missing server URL');
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
// Update provider name
|
|
79
|
-
const providerName = capitalizeFirstLetter(stateParsed.provider);
|
|
80
|
-
$provider.textContent = providerName;
|
|
81
|
-
|
|
82
|
-
// Validate redirect URL
|
|
83
|
-
if (stateParsed.redirectUrl && !webManager.isValidRedirectUrl(stateParsed.redirectUrl)) {
|
|
84
|
-
throw new Error('Invalid redirect URL');
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
// Build tokenize payload
|
|
88
|
-
const payload = {
|
|
89
|
-
state: 'tokenize',
|
|
90
|
-
provider: stateParsed.provider,
|
|
91
|
-
code: code
|
|
92
|
-
};
|
|
93
|
-
|
|
94
|
-
// Add any additional OAuth parameters
|
|
95
|
-
urlParams.forEach((value, key) => {
|
|
96
|
-
if (key !== 'state' && key !== 'code') {
|
|
97
|
-
payload[key] = value;
|
|
98
|
-
}
|
|
99
|
-
});
|
|
47
|
+
// Update provider display (we can't read encrypted state, so use generic text)
|
|
48
|
+
$provider.textContent = 'Provider';
|
|
100
49
|
|
|
101
|
-
|
|
50
|
+
// Build API URL using webManager (no need to read from state)
|
|
51
|
+
const apiUrl = `${webManager.getApiUrl()}/backend-manager/user/oauth2`;
|
|
102
52
|
|
|
103
|
-
//
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
// Call server to complete OAuth flow
|
|
107
|
-
const response = await authorizedFetch(serverUrl, {
|
|
53
|
+
// Send tokenize request with encrypted state
|
|
54
|
+
// Note: tries=1 because auth codes can only be used once
|
|
55
|
+
const response = await authorizedFetch(apiUrl, {
|
|
108
56
|
method: 'POST',
|
|
109
57
|
timeout: 60000,
|
|
110
58
|
response: 'json',
|
|
111
|
-
tries:
|
|
112
|
-
body:
|
|
59
|
+
tries: 1,
|
|
60
|
+
body: {
|
|
61
|
+
action: 'tokenize',
|
|
62
|
+
code: code,
|
|
63
|
+
encryptedState: encryptedState,
|
|
64
|
+
},
|
|
113
65
|
});
|
|
114
66
|
|
|
115
|
-
console.log('Tokenize response:', response);
|
|
116
|
-
|
|
117
67
|
if (!response.success) {
|
|
118
68
|
throw new Error(response.message || 'Failed to complete authorization');
|
|
119
69
|
}
|
|
120
70
|
|
|
121
|
-
|
|
122
|
-
showSuccess(stateParsed.redirectUrl || stateParsed.referrer || '/account#connections');
|
|
71
|
+
showSuccess();
|
|
123
72
|
|
|
124
73
|
} catch (error) {
|
|
125
74
|
console.error('OAuth callback error:', error);
|
|
@@ -128,19 +77,18 @@ async function handleOAuthCallback() {
|
|
|
128
77
|
}
|
|
129
78
|
|
|
130
79
|
// Show success state
|
|
131
|
-
function showSuccess(
|
|
80
|
+
function showSuccess() {
|
|
132
81
|
const $loading = document.getElementById('oauth2-loading');
|
|
133
82
|
const $result = document.getElementById('oauth2-result');
|
|
134
83
|
const $resultSuccess = document.getElementById('result-success');
|
|
135
84
|
|
|
136
|
-
// Hide loading, show result
|
|
137
85
|
$loading.classList.add('d-none');
|
|
138
86
|
$result.classList.remove('d-none');
|
|
139
87
|
$resultSuccess.classList.remove('d-none');
|
|
140
88
|
|
|
141
|
-
// Redirect after delay
|
|
89
|
+
// Redirect to account page after delay
|
|
142
90
|
setTimeout(() => {
|
|
143
|
-
window.location.href =
|
|
91
|
+
window.location.href = '/account#connections';
|
|
144
92
|
}, 500);
|
|
145
93
|
}
|
|
146
94
|
|
|
@@ -152,32 +100,12 @@ function showError(message) {
|
|
|
152
100
|
const $errorMessage = document.getElementById('error-message');
|
|
153
101
|
const $returnButton = document.getElementById('return-button');
|
|
154
102
|
|
|
155
|
-
// Hide loading, show error
|
|
156
103
|
$loading.classList.add('d-none');
|
|
157
104
|
$result.classList.remove('d-none');
|
|
158
105
|
$resultError.classList.remove('d-none');
|
|
159
106
|
|
|
160
|
-
// Set error message
|
|
161
107
|
$errorMessage.textContent = message;
|
|
162
108
|
|
|
163
|
-
//
|
|
164
|
-
|
|
165
|
-
const state = urlParams.get('state');
|
|
166
|
-
|
|
167
|
-
if (state) {
|
|
168
|
-
try {
|
|
169
|
-
const state = JSON.parse(decodeURIComponent(state));
|
|
170
|
-
if (state.referrer) {
|
|
171
|
-
$returnButton.href = state.referrer;
|
|
172
|
-
}
|
|
173
|
-
} catch (e) {
|
|
174
|
-
// Use default
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
// Capitalize first letter
|
|
180
|
-
function capitalizeFirstLetter(string) {
|
|
181
|
-
if (!string) return '';
|
|
182
|
-
return string.charAt(0).toUpperCase() + string.slice(1);
|
|
109
|
+
// Default return URL
|
|
110
|
+
$returnButton.href = '/account#connections';
|
|
183
111
|
}
|
|
@@ -208,7 +208,7 @@ badges:
|
|
|
208
208
|
|
|
209
209
|
<!-- Profile Section -->
|
|
210
210
|
<section id="profile-section" class="account-section d-none">
|
|
211
|
-
<h2 class="h3 mb-4" >My
|
|
211
|
+
<h2 class="h3 mb-4" >My profile</h2>
|
|
212
212
|
|
|
213
213
|
<form id="profile-form" novalidate>
|
|
214
214
|
<!-- Avatar and Edit Button -->
|
|
@@ -244,7 +244,7 @@ badges:
|
|
|
244
244
|
<!-- Account Details Section -->
|
|
245
245
|
<div class="card mb-3">
|
|
246
246
|
<div class="card-body">
|
|
247
|
-
<h5 class="card-title text-body mb-3">Account
|
|
247
|
+
<h5 class="card-title text-body mb-3">Account details</h5>
|
|
248
248
|
|
|
249
249
|
<!-- Email and UID Fields Row -->
|
|
250
250
|
<div class="row g-3">
|
|
@@ -276,7 +276,7 @@ badges:
|
|
|
276
276
|
<!-- Personal Information Section -->
|
|
277
277
|
<div class="card mb-3">
|
|
278
278
|
<div class="card-body">
|
|
279
|
-
<h5 class="card-title text-body mb-3">Personal
|
|
279
|
+
<h5 class="card-title text-body mb-3">Personal information</h5>
|
|
280
280
|
|
|
281
281
|
<!-- Name Fields Row -->
|
|
282
282
|
<div class="row g-3 mb-3">
|
|
@@ -748,7 +748,7 @@ badges:
|
|
|
748
748
|
|
|
749
749
|
<div class="card">
|
|
750
750
|
<div class="card-body">
|
|
751
|
-
<h5 class="card-title">Email
|
|
751
|
+
<h5 class="card-title">Email notifications</h5>
|
|
752
752
|
|
|
753
753
|
<div class="form-check form-switch mb-3">
|
|
754
754
|
<input class="form-check-input" type="checkbox" id="marketing-emails" checked>
|
|
@@ -779,12 +779,12 @@ badges:
|
|
|
779
779
|
|
|
780
780
|
<!-- Security Section -->
|
|
781
781
|
<section id="security-section" class="account-section d-none">
|
|
782
|
-
<h2 class="h3 mb-4" >Security and
|
|
782
|
+
<h2 class="h3 mb-4" >Security and data</h2>
|
|
783
783
|
|
|
784
784
|
<!-- Sign-in Methods Section -->
|
|
785
785
|
<div class="card mb-3">
|
|
786
786
|
<div class="card-body">
|
|
787
|
-
<h5 class="card-title">Sign-in
|
|
787
|
+
<h5 class="card-title">Sign-in methods</h5>
|
|
788
788
|
<p class="text-muted mb-0">Manage how you sign in to your account. You can connect multiple methods for added convenience and security.</p>
|
|
789
789
|
|
|
790
790
|
<div id="signin-methods-list">
|
|
@@ -846,7 +846,7 @@ badges:
|
|
|
846
846
|
<div class="card">
|
|
847
847
|
<div class="card-body">
|
|
848
848
|
<div class="d-flex justify-content-between align-items-center mb-3">
|
|
849
|
-
<h5 class="card-title mb-0">Active
|
|
849
|
+
<h5 class="card-title mb-0">Active sessions</h5>
|
|
850
850
|
<form id="signout-all-sessions-form" class="d-inline" novalidate>
|
|
851
851
|
<input type="hidden" name="action" value="signout-all">
|
|
852
852
|
<button type="submit" class="btn btn-outline-danger btn-sm">
|
|
@@ -873,11 +873,11 @@ badges:
|
|
|
873
873
|
|
|
874
874
|
<!-- Connections Section -->
|
|
875
875
|
<section id="connections-section" class="account-section d-none">
|
|
876
|
-
<h2 class="h3 mb-4" >Connected
|
|
876
|
+
<h2 class="h3 mb-4" >Connected accounts</h2>
|
|
877
877
|
|
|
878
878
|
<div class="card">
|
|
879
879
|
<div class="card-body">
|
|
880
|
-
<h5 class="card-title">Manage
|
|
880
|
+
<h5 class="card-title">Manage connections</h5>
|
|
881
881
|
<p class="text-muted mb-0">Connect your external accounts to access additional features.</p>
|
|
882
882
|
|
|
883
883
|
<div id="connections-list" class="list-group list-group-flush">
|
|
@@ -933,14 +933,14 @@ badges:
|
|
|
933
933
|
|
|
934
934
|
<!-- Billing Section -->
|
|
935
935
|
<section id="billing-section" class="account-section d-none">
|
|
936
|
-
<h2 class="h3 mb-4" >Billing and
|
|
936
|
+
<h2 class="h3 mb-4" >Billing and usage</h2>
|
|
937
937
|
|
|
938
938
|
<div class="card mb-3">
|
|
939
939
|
<div class="card-body">
|
|
940
940
|
<div class="d-flex justify-content-between align-items-center mb-3">
|
|
941
941
|
<div>
|
|
942
942
|
<h5 class="card-title mb-1 d-flex align-items-center">
|
|
943
|
-
<span class="me-2">Current
|
|
943
|
+
<span class="me-2">Current plan</span>
|
|
944
944
|
<span id="plan-status"></span>
|
|
945
945
|
</h5>
|
|
946
946
|
<p class="card-text mb-0">
|
|
@@ -980,7 +980,7 @@ badges:
|
|
|
980
980
|
<div class="card">
|
|
981
981
|
<div class="card-body">
|
|
982
982
|
<div class="d-flex justify-content-between align-items-center mb-3">
|
|
983
|
-
<h5 class="card-title mb-0">Team
|
|
983
|
+
<h5 class="card-title mb-0">Team members</h5>
|
|
984
984
|
<button class="btn btn-primary btn-sm" id="invite-team-member-btn">
|
|
985
985
|
{% uj_icon "user-plus", "me-2" %}
|
|
986
986
|
<span class="button-text">
|
|
@@ -1011,7 +1011,7 @@ badges:
|
|
|
1011
1011
|
<!-- Referral Code Card -->
|
|
1012
1012
|
<div class="card mb-3">
|
|
1013
1013
|
<div class="card-body">
|
|
1014
|
-
<h5 class="card-title">Your
|
|
1014
|
+
<h5 class="card-title">Your referral code</h5>
|
|
1015
1015
|
<p class="card-text text-muted">Share your referral code with friends to earn rewards.</p>
|
|
1016
1016
|
|
|
1017
1017
|
<div class="mb-0">
|
|
@@ -1030,17 +1030,17 @@ badges:
|
|
|
1030
1030
|
<!-- Referrals Stats Card -->
|
|
1031
1031
|
<div class="card">
|
|
1032
1032
|
<div class="card-body">
|
|
1033
|
-
<h5 class="card-title">Referral
|
|
1033
|
+
<h5 class="card-title">Referral statistics</h5>
|
|
1034
1034
|
|
|
1035
1035
|
<!-- Stats Summary -->
|
|
1036
1036
|
<div class="row text-center mb-4">
|
|
1037
1037
|
<div class="col">
|
|
1038
1038
|
<div class="h3 mb-0" id="total-referrals">0</div>
|
|
1039
|
-
<div class="text-muted small">Total
|
|
1039
|
+
<div class="text-muted small">Total referrals</div>
|
|
1040
1040
|
</div>
|
|
1041
1041
|
<div class="col">
|
|
1042
1042
|
<div class="h3 mb-0" id="recent-referrals">0</div>
|
|
1043
|
-
<div class="text-muted small">This
|
|
1043
|
+
<div class="text-muted small">This month</div>
|
|
1044
1044
|
</div>
|
|
1045
1045
|
</div>
|
|
1046
1046
|
|
|
@@ -1050,7 +1050,7 @@ badges:
|
|
|
1050
1050
|
<h2 class="accordion-header" id="referrals-list-heading">
|
|
1051
1051
|
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#referrals-list-collapse" aria-expanded="false" aria-controls="referrals-list-collapse">
|
|
1052
1052
|
<span class="me-2">{% uj_icon "users", "fa-sm" %}</span>
|
|
1053
|
-
View
|
|
1053
|
+
View referral details
|
|
1054
1054
|
<span class="badge bg-primary ms-2" id="referrals-badge">0</span>
|
|
1055
1055
|
</button>
|
|
1056
1056
|
</h2>
|
|
@@ -1072,12 +1072,12 @@ badges:
|
|
|
1072
1072
|
|
|
1073
1073
|
<!-- API Keys Section -->
|
|
1074
1074
|
<section id="api-keys-section" class="account-section d-none">
|
|
1075
|
-
<h2 class="h3 mb-4" >API
|
|
1075
|
+
<h2 class="h3 mb-4" >API keys</h2>
|
|
1076
1076
|
|
|
1077
1077
|
<div class="card">
|
|
1078
1078
|
<div class="card-body">
|
|
1079
1079
|
<div class="d-flex justify-content-between align-items-center mb-3">
|
|
1080
|
-
<h5 class="card-title mb-0">Your API
|
|
1080
|
+
<h5 class="card-title mb-0">Your API key</h5>
|
|
1081
1081
|
<form id="reset-api-key-form" class="d-inline" novalidate>
|
|
1082
1082
|
<button type="submit" class="btn btn-outline-danger btn-sm" id="reset-api-key-btn">
|
|
1083
1083
|
{% uj_icon "rotate", "me-1" %}
|
|
@@ -1091,7 +1091,7 @@ badges:
|
|
|
1091
1091
|
|
|
1092
1092
|
<!-- API Key Display -->
|
|
1093
1093
|
<div class="mb-3">
|
|
1094
|
-
<label for="api-key-input" class="form-label small text-muted">Private API
|
|
1094
|
+
<label for="api-key-input" class="form-label small text-muted">Private API key</label>
|
|
1095
1095
|
<div class="input-group">
|
|
1096
1096
|
<input type="text" class="form-control font-monospace" id="api-key-input" readonly value="Loading...">
|
|
1097
1097
|
<button type="button" class="btn btn-outline-adaptive" id="copy-api-key-btn">
|
|
@@ -1116,7 +1116,7 @@ badges:
|
|
|
1116
1116
|
|
|
1117
1117
|
<!-- Delete Account Section (Hidden by default) -->
|
|
1118
1118
|
<section id="delete-section" class="account-section d-none">
|
|
1119
|
-
<h2 class="h3 mb-4 text-danger" >Delete
|
|
1119
|
+
<h2 class="h3 mb-4 text-danger" >Delete account</h2>
|
|
1120
1120
|
|
|
1121
1121
|
<div class="alert alert-danger">
|
|
1122
1122
|
<h5 class="alert-heading">
|
|
@@ -1135,7 +1135,7 @@ badges:
|
|
|
1135
1135
|
|
|
1136
1136
|
<div class="card border-danger">
|
|
1137
1137
|
<div class="card-body">
|
|
1138
|
-
<h5 class="card-title text-danger">Confirm
|
|
1138
|
+
<h5 class="card-title text-danger">Confirm account deletion</h5>
|
|
1139
1139
|
|
|
1140
1140
|
<form id="delete-account-form" novalidate>
|
|
1141
1141
|
<div class="mb-3">
|