StarrySkyWorld commited on
Commit
fb7f347
Β·
1 Parent(s): c7f27a3

feat: Add secret view

Browse files
Files changed (3) hide show
  1. app/api/autoreg.py +4 -1
  2. app/static/index.html +97 -4
  3. app/websocket.py +4 -1
app/api/autoreg.py CHANGED
@@ -38,7 +38,10 @@ async def _broadcast_accounts_update(ws):
38
  "tokenData": {
39
  "accountName": t.account_name,
40
  "email": t.email,
41
- "expiresAt": t.expires_at.isoformat() if t.expires_at else None
 
 
 
42
  },
43
  "usage": {
44
  "currentUsage": -1,
 
38
  "tokenData": {
39
  "accountName": t.account_name,
40
  "email": t.email,
41
+ "expiresAt": t.expires_at.isoformat() if t.expires_at else None,
42
+ "refreshToken": t.raw_data.get('refreshToken') if t.raw_data else None,
43
+ "clientId": t.raw_data.get('_clientId') if t.raw_data else None,
44
+ "clientSecret": t.raw_data.get('_clientSecret') if t.raw_data else None
45
  },
46
  "usage": {
47
  "currentUsage": -1,
app/static/index.html CHANGED
@@ -6671,8 +6671,40 @@
6671
  <textarea class="modal-textarea" id="ssoTokenInput" placeholder="Paste cookie..."></textarea>
6672
  <button class="btn btn-primary" style="width:100%" onclick="importSsoToken()">Import</button>
6673
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6674
  </div>
6675
  </div>
 
 
 
6676
 
6677
 
6678
  <div class="dialog-overlay" id="dialogOverlay" onclick="if(event.target === this) closeDialog()">
@@ -6952,6 +6984,8 @@ let pendingAction = null;
6952
 
6953
 
6954
  const STATE = {"filter":"all","sort":"email","compact":false,"settingsOpen":false,"searchQuery":"","scrollPosition":0,"activeTab":"accounts","disableToasts":false};
 
 
6955
 
6956
  function getState() { return STATE; }
6957
 
@@ -7453,6 +7487,59 @@ let pendingAction = null;
7453
  closeSsoModal();
7454
  }
7455
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7456
 
7457
  // === Console Drawer ===
7458
 
@@ -8053,6 +8140,10 @@ let pendingAction = null;
8053
  case 'accountsLoaded':
8054
  // Standalone WebSocket sends accountsLoaded with accounts array
8055
  if (msg.accounts && Array.isArray(msg.accounts)) {
 
 
 
 
8056
  const list = document.getElementById('accountList');
8057
  if (list) {
8058
  if (msg.accounts.length === 0) {
@@ -8064,11 +8155,12 @@ let pendingAction = null;
8064
  const statusClass = acc.isExpired ? 'expired' : (acc.needsRefresh ? 'warning' : 'valid');
8065
  const name = acc.tokenData?.accountName || acc.filename || 'Unknown';
8066
  const email = acc.tokenData?.email || '';
 
8067
  const usage = acc.usage || {};
8068
  const usageText = usage.currentUsage >= 0 ? usage.currentUsage + '/' + (usage.usageLimit || 500) : 'β€”';
8069
  const usagePercent = usage.percentageUsed || 0;
8070
 
8071
- return '<div class="account ' + isActive + ' ' + statusClass + '" data-filename="' + acc.filename + '" onclick="switchAccount(\'' + acc.filename + '\')">' +
8072
  '<div class="account-info">' +
8073
  '<div class="account-name">' + name + '</div>' +
8074
  '<div class="account-email">' + email + '</div>' +
@@ -8078,8 +8170,9 @@ let pendingAction = null;
8078
  '<div class="usage-text">' + usageText + '</div>' +
8079
  '</div>' +
8080
  '<div class="account-actions">' +
8081
- '<button class="btn btn-sm" onclick="event.stopPropagation(); refreshToken(\'' + acc.filename + '\')">πŸ”„</button>' +
8082
- '<button class="btn btn-sm btn-danger" onclick="event.stopPropagation(); confirmDelete(\'' + acc.filename + '\')">πŸ—‘οΈ</button>' +
 
8083
  '</div>' +
8084
  '</div>';
8085
  }).join('');
@@ -9426,4 +9519,4 @@ let pendingAction = null;
9426
  window.updateScheduledRegState = updateScheduledRegState;
9427
  </script>
9428
  </body>
9429
- </html>
 
6671
  <textarea class="modal-textarea" id="ssoTokenInput" placeholder="Paste cookie..."></textarea>
6672
  <button class="btn btn-primary" style="width:100%" onclick="importSsoToken()">Import</button>
6673
  </div>
6674
+
6675
+ <div class="modal-overlay" id="accountSecretsModal" onclick="if(event.target === this) closeAccountSecrets()">
6676
+ <div class="modal">
6677
+ <div class="modal-header">
6678
+ <span class="modal-title">Account Secrets</span>
6679
+ <button class="modal-close" onclick="closeAccountSecrets()">ΠŽΠ‘</button>
6680
+ </div>
6681
+ <div class="modal-body">
6682
+ <div class="form-group">
6683
+ <label class="form-label">Email</label>
6684
+ <textarea class="modal-textarea" id="accountSecretEmail" readonly></textarea>
6685
+ <button class="btn btn-secondary" style="width:100%" onclick="copySecretField('accountSecretEmail')">Copy</button>
6686
+ </div>
6687
+ <div class="form-group">
6688
+ <label class="form-label">RefreshToken</label>
6689
+ <textarea class="modal-textarea" id="accountSecretRefreshToken" readonly></textarea>
6690
+ <button class="btn btn-secondary" style="width:100%" onclick="copySecretField('accountSecretRefreshToken')">Copy</button>
6691
+ </div>
6692
+ <div class="form-group">
6693
+ <label class="form-label">Client ID</label>
6694
+ <textarea class="modal-textarea" id="accountSecretClientId" readonly></textarea>
6695
+ <button class="btn btn-secondary" style="width:100%" onclick="copySecretField('accountSecretClientId')">Copy</button>
6696
+ </div>
6697
+ <div class="form-group">
6698
+ <label class="form-label">Client Secret</label>
6699
+ <textarea class="modal-textarea" id="accountSecretClientSecret" readonly></textarea>
6700
+ <button class="btn btn-secondary" style="width:100%" onclick="copySecretField('accountSecretClientSecret')">Copy</button>
6701
+ </div>
6702
+ </div>
6703
  </div>
6704
  </div>
6705
+
6706
+ </div>
6707
+ </div>
6708
 
6709
 
6710
  <div class="dialog-overlay" id="dialogOverlay" onclick="if(event.target === this) closeDialog()">
 
6984
 
6985
 
6986
  const STATE = {"filter":"all","sort":"email","compact":false,"settingsOpen":false,"searchQuery":"","scrollPosition":0,"activeTab":"accounts","disableToasts":false};
6987
+
6988
+ let ACCOUNT_INDEX = {};
6989
 
6990
  function getState() { return STATE; }
6991
 
 
7487
  closeSsoModal();
7488
  }
7489
  }
7490
+ // === Account Secrets Modal ===
7491
+
7492
+ function openAccountSecrets(filename) {
7493
+ const modal = document.getElementById('accountSecretsModal');
7494
+ const acc = ACCOUNT_INDEX[filename];
7495
+ if (!modal || !acc) return;
7496
+
7497
+ const tokenData = acc.tokenData || {};
7498
+ const email = tokenData.email || '';
7499
+ const refreshToken = tokenData.refreshToken || '';
7500
+ const clientId = tokenData.clientId || '';
7501
+ const clientSecret = tokenData.clientSecret || '';
7502
+
7503
+ const emailEl = document.getElementById('accountSecretEmail');
7504
+ const refreshEl = document.getElementById('accountSecretRefreshToken');
7505
+ const clientIdEl = document.getElementById('accountSecretClientId');
7506
+ const clientSecretEl = document.getElementById('accountSecretClientSecret');
7507
+
7508
+ if (emailEl) emailEl.value = email;
7509
+ if (refreshEl) refreshEl.value = refreshToken;
7510
+ if (clientIdEl) clientIdEl.value = clientId;
7511
+ if (clientSecretEl) clientSecretEl.value = clientSecret;
7512
+
7513
+ modal.classList.add('visible');
7514
+ }
7515
+
7516
+ function closeAccountSecrets() {
7517
+ const modal = document.getElementById('accountSecretsModal');
7518
+ modal?.classList.remove('visible');
7519
+ }
7520
+
7521
+ function copySecretField(id) {
7522
+ const el = document.getElementById(id);
7523
+ const value = el?.value || '';
7524
+ if (!value) return;
7525
+ if (navigator.clipboard?.writeText) {
7526
+ navigator.clipboard.writeText(value).then(() => {
7527
+ showToast('Copied', 'success');
7528
+ }).catch(() => {
7529
+ showToast('Copy failed', 'error');
7530
+ });
7531
+ } else {
7532
+ try {
7533
+ el.focus();
7534
+ el.select();
7535
+ document.execCommand('copy');
7536
+ showToast('Copied', 'success');
7537
+ } catch {
7538
+ showToast('Copy failed', 'error');
7539
+ }
7540
+ }
7541
+ }
7542
+
7543
 
7544
  // === Console Drawer ===
7545
 
 
8140
  case 'accountsLoaded':
8141
  // Standalone WebSocket sends accountsLoaded with accounts array
8142
  if (msg.accounts && Array.isArray(msg.accounts)) {
8143
+ ACCOUNT_INDEX = {};
8144
+ msg.accounts.forEach(function(acc) {
8145
+ ACCOUNT_INDEX[acc.filename] = acc;
8146
+ });
8147
  const list = document.getElementById('accountList');
8148
  if (list) {
8149
  if (msg.accounts.length === 0) {
 
8155
  const statusClass = acc.isExpired ? 'expired' : (acc.needsRefresh ? 'warning' : 'valid');
8156
  const name = acc.tokenData?.accountName || acc.filename || 'Unknown';
8157
  const email = acc.tokenData?.email || '';
8158
+ const safeFilename = (acc.filename || '').replace(/'/g, "\\'");
8159
  const usage = acc.usage || {};
8160
  const usageText = usage.currentUsage >= 0 ? usage.currentUsage + '/' + (usage.usageLimit || 500) : 'β€”';
8161
  const usagePercent = usage.percentageUsed || 0;
8162
 
8163
+ return '<div class="account ' + isActive + ' ' + statusClass + '" data-filename="' + acc.filename + '" onclick="switchAccount(\'' + safeFilename + '\')">' +
8164
  '<div class="account-info">' +
8165
  '<div class="account-name">' + name + '</div>' +
8166
  '<div class="account-email">' + email + '</div>' +
 
8170
  '<div class="usage-text">' + usageText + '</div>' +
8171
  '</div>' +
8172
  '<div class="account-actions">' +
8173
+ '<button class="btn btn-sm" onclick="event.stopPropagation(); refreshToken(\'' + safeFilename + '\')">πŸ”„</button>' +
8174
+ '<button class="btn btn-sm" onclick="event.stopPropagation(); openAccountSecrets(\'' + safeFilename + '\')">πŸ”‘</button>' +
8175
+ '<button class="btn btn-sm btn-danger" onclick="event.stopPropagation(); confirmDelete(\'' + safeFilename + '\')">πŸ—‘οΈ</button>' +
8176
  '</div>' +
8177
  '</div>';
8178
  }).join('');
 
9519
  window.updateScheduledRegState = updateScheduledRegState;
9520
  </script>
9521
  </body>
9522
+ </html>
app/websocket.py CHANGED
@@ -170,7 +170,10 @@ async def handle_command(command: str, data: Dict[str, Any], websocket: WebSocke
170
  "tokenData": {
171
  "accountName": token.account_name,
172
  "email": token.email,
173
- "expiresAt": token.expires_at.isoformat() if token.expires_at else None
 
 
 
174
  },
175
  "usage": {
176
  "currentUsage": -1,
 
170
  "tokenData": {
171
  "accountName": token.account_name,
172
  "email": token.email,
173
+ "expiresAt": token.expires_at.isoformat() if token.expires_at else None,
174
+ "refreshToken": token.raw_data.get('refreshToken') if token.raw_data else None,
175
+ "clientId": token.raw_data.get('_clientId') if token.raw_data else None,
176
+ "clientSecret": token.raw_data.get('_clientSecret') if token.raw_data else None
177
  },
178
  "usage": {
179
  "currentUsage": -1,