tide-commander 1.165.1 → 1.166.0
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/CHANGELOG.md +9 -0
- package/dist/assets/{BossLogsModal-IqVqHwz-.js → BossLogsModal-CsMSjCZp.js} +1 -1
- package/dist/assets/{BossSpawnModal-Dj9ayWfR.js → BossSpawnModal-Z6ZZuraP.js} +1 -1
- package/dist/assets/{ControlsModal-ki85jvDA.js → ControlsModal-UAWrBPnS.js} +1 -1
- package/dist/assets/{DockerLogsModal-CeIuKlvk.js → DockerLogsModal-Dvk09sbP.js} +1 -1
- package/dist/assets/{EmbeddedEditor-BA9mX_Lg.js → EmbeddedEditor-n2QnK4hA.js} +1 -1
- package/dist/assets/{GmailOAuthSetup-7nj6aLot.js → GmailOAuthSetup-DhKifdH0.js} +1 -1
- package/dist/assets/GoogleOAuthSetup-D14lS0lT.js +270 -0
- package/dist/assets/{IframeModal-DkkCx6nq.js → IframeModal-c2Go5TIq.js} +1 -1
- package/dist/assets/{IntegrationsPanel-DHjpWEFF.js → IntegrationsPanel-CkZyLCWN.js} +2 -2
- package/dist/assets/{LogViewerModal-DKYB8dHo.js → LogViewerModal-onBz0hK3.js} +1 -1
- package/dist/assets/{MonitoringModal-DOKMIa5U.js → MonitoringModal-D58Oka-L.js} +1 -1
- package/dist/assets/{PM2LogsModal-XlbT9uoY.js → PM2LogsModal-DtEHMB6v.js} +1 -1
- package/dist/assets/{RestoreArchivedAreaModal-Du1f8Urv.js → RestoreArchivedAreaModal-BKgjpTYA.js} +1 -1
- package/dist/assets/{Scene2DCanvas-BTnpyyK6.js → Scene2DCanvas-BSe1eM97.js} +1 -1
- package/dist/assets/{SceneManager-owoMSHKT.js → SceneManager-Bv_J6Asr.js} +1 -1
- package/dist/assets/{SkillsPanel-BKjpKM1N.js → SkillsPanel-aK_Vhyhx.js} +1 -1
- package/dist/assets/{SlackMultiInstanceSetup-C0k60ioO.js → SlackMultiInstanceSetup-CR9tuDCR.js} +1 -1
- package/dist/assets/{SpawnModal-Cww_6dcz.js → SpawnModal-BGoN5C1p.js} +1 -1
- package/dist/assets/{StatisticsModal-Dl5loLLI.js → StatisticsModal-BY9pHl-m.js} +1 -1
- package/dist/assets/{SubordinateAssignmentModal-DefQL38f.js → SubordinateAssignmentModal-2bO3Y-zS.js} +1 -1
- package/dist/assets/{TriggerManagerPanel-RWrID12k.js → TriggerManagerPanel-CtDALyfM.js} +1 -1
- package/dist/assets/{WorkflowEditorPanel-Ccwm4H5t.js → WorkflowEditorPanel-CEYKw625.js} +1 -1
- package/dist/assets/{area-logos-DnrrrkFh.js → area-logos-HooY4aHb.js} +1 -1
- package/dist/assets/{index-D-MKKw7S.js → index-BSPc0AE-.js} +3 -3
- package/dist/assets/{index-Fcs4x2Vj.js → index-BTaesMkc.js} +1 -1
- package/dist/assets/{index-Cjgy2CnG.js → index-BxPFO5EJ.js} +1 -1
- package/dist/assets/{index-B5iakYkB.js → index-CCZsNuvk.js} +1 -1
- package/dist/assets/{index-CcHBNDP_.js → index-CUfnGcms.js} +2 -2
- package/dist/assets/{index-DG7HgtQn.js → index-CwjljR04.js} +1 -1
- package/dist/assets/{index-CbJ-R04c.js → index-DPJj90PK.js} +1 -1
- package/dist/assets/{index-DZ7i37eV.js → index-DdCk239w.js} +1 -1
- package/dist/assets/{index-g0yAcpoy.js → index-I8PSiTLl.js} +1 -1
- package/dist/assets/main-BzGnAbiA.css +1 -0
- package/dist/assets/{main-DIf9R6Jx.js → main-D6IBl2Xu.js} +8 -8
- package/dist/assets/{web-DhZ0XCan.js → web-B6Lyhp8u.js} +1 -1
- package/dist/assets/{web-DDxnPoTK.js → web-BVrXaF9j.js} +1 -1
- package/dist/assets/{web-Dlc8Nyra.js → web-DQi4Jy_X.js} +1 -1
- package/dist/build-info.json +1 -1
- package/dist/index.html +2 -2
- package/dist/src/packages/server/integrations/gmail/gmail-client.js +73 -5
- package/dist/src/packages/server/integrations/gmail/gmail-routes.js +9 -4
- package/dist/src/packages/server/integrations/google-auth/token-health.js +196 -0
- package/dist/src/packages/server/integrations/google-calendar/calendar-client.js +61 -6
- package/dist/src/packages/server/integrations/google-calendar/calendar-routes.js +9 -4
- package/dist/src/packages/server/integrations/google-drive/drive-client.js +61 -6
- package/dist/src/packages/server/integrations/google-drive/drive-routes.js +9 -4
- package/package.json +1 -1
- package/dist/assets/GoogleOAuthSetup-Bh-Cy-dE.js +0 -236
- package/dist/assets/main-Dnp-3iMX.css +0 -1
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import { google } from 'googleapis';
|
|
8
8
|
import { loadConfig, updateConfig } from './drive-config.js';
|
|
9
|
+
import { getTokenHealth, needsReauth, notifyTokenRotated, onTokenRotated, probeTokenHealth, reportGoogleApiError, reportGoogleApiSuccess, startTokenHealthMonitor, } from '../google-auth/token-health.js';
|
|
9
10
|
import { Readable } from 'stream';
|
|
10
11
|
// ─── State ───
|
|
11
12
|
let ctx = null;
|
|
@@ -22,6 +23,30 @@ const SCOPES = [
|
|
|
22
23
|
];
|
|
23
24
|
const REDIRECT_PATH = '/api/drive/auth/callback';
|
|
24
25
|
let oauth2Client = null;
|
|
26
|
+
/**
|
|
27
|
+
* Build an OAuth2 client whose every outbound call updates shared token health.
|
|
28
|
+
*
|
|
29
|
+
* Every Drive and Docs call made with `auth: oauth2Client` funnels through this one
|
|
30
|
+
* `request()` method, so wrapping it here means a revoked token is detected the moment
|
|
31
|
+
* an agent actually touches Drive — no waiting for the next background probe — without
|
|
32
|
+
* instrumenting each of the ~15 API call sites.
|
|
33
|
+
*/
|
|
34
|
+
function createOAuthClient(clientId, clientSecret, redirectUri) {
|
|
35
|
+
const client = new google.auth.OAuth2(clientId, clientSecret, redirectUri);
|
|
36
|
+
const originalRequest = client.request.bind(client);
|
|
37
|
+
client.request = (async (opts) => {
|
|
38
|
+
try {
|
|
39
|
+
const result = await originalRequest(opts);
|
|
40
|
+
reportGoogleApiSuccess();
|
|
41
|
+
return result;
|
|
42
|
+
}
|
|
43
|
+
catch (err) {
|
|
44
|
+
reportGoogleApiError(err);
|
|
45
|
+
throw err;
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
return client;
|
|
49
|
+
}
|
|
25
50
|
function getRedirectUri() {
|
|
26
51
|
if (!ctx)
|
|
27
52
|
throw new Error('Google Drive not initialized');
|
|
@@ -44,10 +69,16 @@ export async function init(integrationCtx) {
|
|
|
44
69
|
ctx.log.info('Google Drive missing OAuth credentials, skipping init');
|
|
45
70
|
return;
|
|
46
71
|
}
|
|
47
|
-
oauth2Client =
|
|
72
|
+
oauth2Client = createOAuthClient(clientId, clientSecret, getRedirectUri());
|
|
48
73
|
oauth2Client.setCredentials({ refresh_token: refreshToken });
|
|
49
74
|
driveApi = google.drive({ version: 'v3', auth: oauth2Client });
|
|
50
75
|
docsApi = google.docs({ version: 'v1', auth: oauth2Client });
|
|
76
|
+
// Constructing the client above proves nothing — it just wraps the token string.
|
|
77
|
+
// Probe Google (and keep probing) so an expired token is reflected in getStatus().
|
|
78
|
+
startTokenHealthMonitor(ctx);
|
|
79
|
+
// Re-read the shared refresh token if the user re-consents via Gmail or Calendar,
|
|
80
|
+
// otherwise this client keeps using the token it was seeded with here.
|
|
81
|
+
onTokenRotated('google-drive', () => init(integrationCtx));
|
|
51
82
|
ctx.log.info('Google Drive initialized');
|
|
52
83
|
}
|
|
53
84
|
export async function shutdown() {
|
|
@@ -61,16 +92,36 @@ export function getStatus() {
|
|
|
61
92
|
const hasCredentials = !!(ctx?.secrets.get('GOOGLE_CLIENT_ID') &&
|
|
62
93
|
ctx?.secrets.get('GOOGLE_CLIENT_SECRET') &&
|
|
63
94
|
ctx?.secrets.get('GOOGLE_REFRESH_TOKEN'));
|
|
95
|
+
const health = getTokenHealth();
|
|
96
|
+
const reauth = hasCredentials && needsReauth(health);
|
|
97
|
+
// Having credentials on disk is necessary but not sufficient — a revoked refresh
|
|
98
|
+
// token is still three non-empty strings. Only report connected when Google's last
|
|
99
|
+
// verdict wasn't a rejection. An 'unreachable' probe (offline host) is deliberately
|
|
100
|
+
// NOT treated as a rejection: it would push users into a pointless re-consent flow.
|
|
101
|
+
const wired = config.enabled && hasCredentials && driveApi !== null;
|
|
102
|
+
let error;
|
|
103
|
+
if (!hasCredentials && config.enabled)
|
|
104
|
+
error = 'Missing OAuth credentials';
|
|
105
|
+
else if (reauth)
|
|
106
|
+
error = health.error;
|
|
64
107
|
return {
|
|
65
|
-
authenticated: Boolean(driveApi && hasCredentials),
|
|
66
|
-
connected:
|
|
67
|
-
lastChecked: Date.now(),
|
|
68
|
-
error
|
|
108
|
+
authenticated: Boolean(driveApi && hasCredentials) && !reauth,
|
|
109
|
+
connected: wired && !reauth,
|
|
110
|
+
lastChecked: health.checkedAt || Date.now(),
|
|
111
|
+
error,
|
|
112
|
+
needsReauth: reauth,
|
|
113
|
+
tokenState: hasCredentials ? health.state : undefined,
|
|
69
114
|
};
|
|
70
115
|
}
|
|
71
116
|
export function isConfigured() {
|
|
72
117
|
return driveApi !== null;
|
|
73
118
|
}
|
|
119
|
+
/** Force a live refresh_token exchange against Google and return the updated status. */
|
|
120
|
+
export async function probeToken() {
|
|
121
|
+
if (ctx)
|
|
122
|
+
await probeTokenHealth(ctx, { force: true });
|
|
123
|
+
return getStatus();
|
|
124
|
+
}
|
|
74
125
|
// ─── OAuth ───
|
|
75
126
|
export function getAuthUrl() {
|
|
76
127
|
if (!oauth2Client) {
|
|
@@ -79,7 +130,7 @@ export function getAuthUrl() {
|
|
|
79
130
|
if (!clientId || !clientSecret || !ctx) {
|
|
80
131
|
throw new Error('Google Drive OAuth not configured');
|
|
81
132
|
}
|
|
82
|
-
oauth2Client =
|
|
133
|
+
oauth2Client = createOAuthClient(clientId, clientSecret, getRedirectUri());
|
|
83
134
|
}
|
|
84
135
|
return oauth2Client.generateAuthUrl({
|
|
85
136
|
access_type: 'offline',
|
|
@@ -99,6 +150,10 @@ export async function handleAuthCallback(code) {
|
|
|
99
150
|
docsApi = google.docs({ version: 'v1', auth: oauth2Client });
|
|
100
151
|
// Auto-enable the integration after successful OAuth
|
|
101
152
|
updateConfig({ enabled: true });
|
|
153
|
+
// Fresh consent just produced this token. Clears the 'expired' verdict AND re-arms
|
|
154
|
+
// Gmail and Calendar, whose clients still hold the old refresh token — all three
|
|
155
|
+
// share this one secret, so one consent must repair all three.
|
|
156
|
+
await notifyTokenRotated();
|
|
102
157
|
ctx.log.info('Google Drive OAuth complete. Drive initialized.');
|
|
103
158
|
}
|
|
104
159
|
// ─── Files CRUD ───
|
|
@@ -283,10 +283,15 @@ router.get('/search', async (req, res) => {
|
|
|
283
283
|
}
|
|
284
284
|
});
|
|
285
285
|
// ─── Status & Auth ───
|
|
286
|
-
// GET /api/drive/status — Get Google Drive auth status
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
286
|
+
// GET /api/drive/status — Get Google Drive auth status.
|
|
287
|
+
// `?probe=1` forces a live refresh_token exchange against Google instead of reusing
|
|
288
|
+
// the cached verdict — the settings UI uses it to verify the token on open, so a
|
|
289
|
+
// revoked token can't keep rendering as "Connected".
|
|
290
|
+
router.get('/status', async (req, res) => {
|
|
291
|
+
if (req.query.probe === '1' || req.query.probe === 'true') {
|
|
292
|
+
await driveClient.probeToken();
|
|
293
|
+
}
|
|
294
|
+
res.json(driveClient.getStatus());
|
|
290
295
|
});
|
|
291
296
|
// GET /api/drive/auth/url — Get OAuth authorization URL
|
|
292
297
|
router.get('/auth/url', (_req, res) => {
|
package/package.json
CHANGED
|
@@ -1,236 +0,0 @@
|
|
|
1
|
-
import{r as t,i as w,k as v,j as e}from"./main-DIf9R6Jx.js";import"./vendor-react--Eh9ivFN.js";import"./vendor-three-Chj50gSY.js";function B({integration:o,onSave:j,onCancel:h}){const[l,C]=t.useState(o.values.GOOGLE_CLIENT_ID||""),[p,E]=t.useState(""),[b,S]=t.useState(o.values.GOOGLE_REDIRECT_BASE_URL||""),[y,G]=t.useState(null),[g,O]=t.useState(null),[c,u]=t.useState(!1),[N,s]=t.useState(null),[A,z]=t.useState(!1),I=o.values.GOOGLE_CLIENT_ID==="********"&&o.values.GOOGLE_CLIENT_SECRET==="********",[x,f]=t.useState(o.status.connected?"connected":"credentials"),r=t.useRef(null),m=t.useCallback(async()=>{try{const n={gmail:"/api/email/status","google-calendar":"/api/calendar/status","google-drive":"/api/drive/status"}[o.id]||"/api/calendar/status",i=await w(v(n));if(i.ok){const d=await i.json();O(d),d.authenticated&&(f("connected"),r.current&&(clearInterval(r.current),r.current=null))}}catch(a){console.error("Failed to fetch Google auth status:",a)}},[o.id]);t.useEffect(()=>(m(),()=>{r.current&&clearInterval(r.current)}),[m]);const k=async()=>{const n={gmail:"/api/email/auth/url","google-calendar":"/api/calendar/auth/url","google-drive":"/api/drive/auth/url"}[o.id]||"/api/calendar/auth/url",i=await w(v(n));if(!i.ok){const _=await i.json().catch(()=>({}));throw new Error(_.error||`HTTP ${i.status}`)}const d=await i.json();if(!d.url)throw new Error("OAuth URL is empty");G(d.url),s(null),f("authorize"),r.current&&clearInterval(r.current),r.current=setInterval(m,3e3)},L=async()=>{u(!0),s(null);try{await k()}catch(a){s(a instanceof Error?a.message:"Failed to get OAuth URL")}finally{u(!1)}},R=async()=>{if(!l.trim()){s("Client ID is required");return}u(!0),s(null);try{const a={GOOGLE_REDIRECT_BASE_URL:b.trim()};l.trim()&&l.trim()!=="********"&&(a.GOOGLE_CLIENT_ID=l.trim()),p.trim()&&(a.GOOGLE_CLIENT_SECRET=p.trim()),console.log("Saving Google OAuth config...",a),await j(a),console.log("Config saved successfully"),await k()}catch(a){const n=a instanceof Error?a.message:"Failed to get OAuth URL";console.error("Error in handleSaveCredentials:",n,a),s(n)}finally{u(!1)}};return e.jsxs("div",{className:"google-oauth-setup",children:[N&&e.jsx("div",{className:"google-oauth-error",children:N}),x==="credentials"&&e.jsxs("div",{className:"google-oauth-section",children:[e.jsx("h4",{className:"google-oauth-section-title",children:"Google OAuth Credentials"}),I&&!A?e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"google-oauth-shared-banner",children:[e.jsxs("div",{className:"google-oauth-shared-title",children:[e.jsx("span",{children:"✓"}),e.jsx("span",{children:"Credentials already configured"})]}),e.jsx("div",{className:"google-oauth-shared-body",children:"Existing Google OAuth credentials (Client ID and Secret) are saved in your secrets store — they're shared between Gmail, Calendar, and Drive. You may still need to re-authorize once so the refresh token grants access to this service."})]}),e.jsxs("div",{className:"integration-form-actions",children:[e.jsx("button",{type:"button",className:"integration-btn cancel",onClick:h,children:"Cancel"}),e.jsx("button",{type:"button",className:"integration-btn secondary",onClick:()=>z(!0),disabled:c,children:"Edit credentials"}),e.jsx("button",{type:"button",className:"integration-btn save",onClick:L,disabled:c,children:c?"Loading...":"Authorize with existing credentials"})]})]}):e.jsxs(e.Fragment,{children:[e.jsxs("p",{className:"google-oauth-help",children:["Create OAuth2 credentials in the"," ",e.jsx("a",{href:"https://console.cloud.google.com/apis/credentials",target:"_blank",rel:"noopener noreferrer",className:"google-oauth-link",children:"Google Cloud Console"}),". Enable the relevant API (Gmail, Calendar, or Drive). Set the redirect URI to:"," ",e.jsx("code",{className:"google-oauth-code",children:(()=>{const a=o.id==="gmail"?"/api/email/auth/callback":o.id==="google-drive"?"/api/drive/auth/callback":"/api/calendar/auth/callback",n=b.trim();return n?`${n.replace(/\/$/,"")}${a}`:v(a)})()})]}),e.jsxs("div",{className:"google-oauth-field",children:[e.jsxs("label",{className:"integration-field-label",children:["OAuth Client ID ",e.jsx("span",{className:"integration-field-required",children:"*"})]}),e.jsx("input",{type:"text",className:"integration-field-input",value:l,placeholder:"xxxx.apps.googleusercontent.com",onChange:a=>C(a.target.value)})]}),e.jsxs("div",{className:"google-oauth-field",children:[e.jsxs("label",{className:"integration-field-label",children:["OAuth Client Secret ",e.jsx("span",{className:"integration-field-required",children:"*"})]}),e.jsx("input",{type:"password",className:"integration-field-input",value:p,placeholder:o.values.GOOGLE_CLIENT_SECRET?"(saved)":"Enter client secret",onChange:a=>E(a.target.value),autoComplete:"off"})]}),e.jsxs("div",{className:"google-oauth-field",children:[e.jsx("label",{className:"integration-field-label",children:"OAuth Redirect Base URL"}),e.jsx("input",{type:"text",className:"integration-field-input",value:b,placeholder:"http://localhost:6200",onChange:a=>S(a.target.value),autoComplete:"off"}),e.jsxs("span",{className:"integration-field-description",style:{display:"block",marginTop:7,color:"#7f849c",fontSize:12},children:["Override the base URL used to build the OAuth redirect (e.g. ",e.jsx("code",{children:"http://commander.local:10003"}),"). Shared across Gmail, Calendar, and Drive. Leave empty to use the server's local address. Google does not accept raw IPs — use a domain (you can map one in ",e.jsx("code",{children:"/etc/hosts"}),")."]})]}),e.jsxs("div",{className:"integration-form-actions",children:[e.jsx("button",{type:"button",className:"integration-btn cancel",onClick:h,children:"Cancel"}),e.jsx("button",{type:"button",className:"integration-btn save",onClick:R,disabled:c||!l.trim(),children:c?"Saving...":"Save & Authorize"})]})]})]}),x==="authorize"&&e.jsxs("div",{className:"google-oauth-section",children:[e.jsx("h4",{className:"google-oauth-section-title",children:"Authorize Google Access"}),e.jsx("p",{className:"google-oauth-help",children:"Click the link below to authorize Tide Commander to access your Google account. After granting access, you will be redirected back and the connection will be established automatically."}),y&&e.jsx("a",{href:y,target:"_blank",rel:"noopener noreferrer",className:"google-oauth-authorize-btn",children:"Authorize with Google"}),e.jsxs("div",{className:"google-oauth-waiting",children:[e.jsx("span",{className:"google-oauth-spinner"}),e.jsx("span",{children:"Waiting for authorization to complete..."})]}),e.jsx("div",{className:"integration-form-actions",children:e.jsx("button",{type:"button",className:"integration-btn cancel",onClick:()=>{r.current&&(clearInterval(r.current),r.current=null),f("credentials")},children:"Back"})})]}),x==="connected"&&e.jsxs("div",{className:"google-oauth-section",children:[e.jsx("h4",{className:"google-oauth-section-title",children:"Google Connected"}),e.jsxs("div",{className:"google-oauth-connected-info",children:[e.jsx("span",{className:"google-oauth-connected-badge",children:"Connected"}),(g==null?void 0:g.emailAddress)&&e.jsx("span",{className:"google-oauth-email",children:g.emailAddress})]}),e.jsx("p",{className:"google-oauth-help",children:"Your Google account has been successfully connected."}),e.jsx("div",{className:"integration-form-actions",children:e.jsx("button",{type:"button",className:"integration-btn cancel",onClick:async()=>{await j({}),h()},children:"Close"})})]}),e.jsx("style",{children:`
|
|
2
|
-
.google-oauth-setup {
|
|
3
|
-
padding: 0;
|
|
4
|
-
}
|
|
5
|
-
.google-oauth-error {
|
|
6
|
-
background: linear-gradient(135deg, rgba(243, 139, 168, 0.2) 0%, rgba(243, 139, 168, 0.08) 100%);
|
|
7
|
-
border-left: 3px solid #f38ba8;
|
|
8
|
-
color: #f38ba8;
|
|
9
|
-
padding: 12px 16px;
|
|
10
|
-
border-radius: 8px;
|
|
11
|
-
margin-bottom: 18px;
|
|
12
|
-
font-size: 13px;
|
|
13
|
-
font-weight: 500;
|
|
14
|
-
}
|
|
15
|
-
.google-oauth-section {
|
|
16
|
-
background: linear-gradient(180deg, rgba(45, 49, 69, 0.4) 0%, rgba(30, 30, 46, 0.2) 100%);
|
|
17
|
-
border: 1px solid rgba(137, 180, 250, 0.15);
|
|
18
|
-
border-radius: 12px;
|
|
19
|
-
padding: 28px;
|
|
20
|
-
backdrop-filter: blur(10px);
|
|
21
|
-
}
|
|
22
|
-
.google-oauth-section-title {
|
|
23
|
-
margin: 0 0 16px 0;
|
|
24
|
-
color: #cdd6f4;
|
|
25
|
-
font-size: 18px;
|
|
26
|
-
font-weight: 700;
|
|
27
|
-
letter-spacing: -0.5px;
|
|
28
|
-
}
|
|
29
|
-
.google-oauth-help {
|
|
30
|
-
color: #a6adc8;
|
|
31
|
-
font-size: 13px;
|
|
32
|
-
line-height: 1.6;
|
|
33
|
-
margin: 0 0 20px 0;
|
|
34
|
-
font-weight: 400;
|
|
35
|
-
}
|
|
36
|
-
.google-oauth-link {
|
|
37
|
-
color: #89b4fa;
|
|
38
|
-
text-decoration: none;
|
|
39
|
-
border-bottom: 1.5px solid rgba(137, 180, 250, 0.4);
|
|
40
|
-
transition: all 0.2s ease;
|
|
41
|
-
font-weight: 500;
|
|
42
|
-
}
|
|
43
|
-
.google-oauth-link:hover {
|
|
44
|
-
color: #a8c5ff;
|
|
45
|
-
border-bottom-color: #89b4fa;
|
|
46
|
-
}
|
|
47
|
-
.google-oauth-code {
|
|
48
|
-
background: rgba(137, 180, 250, 0.15);
|
|
49
|
-
color: #89b4fa;
|
|
50
|
-
padding: 4px 10px;
|
|
51
|
-
border-radius: 6px;
|
|
52
|
-
font-size: 12px;
|
|
53
|
-
word-break: break-all;
|
|
54
|
-
font-weight: 500;
|
|
55
|
-
font-family: 'Monaco', 'Menlo', monospace;
|
|
56
|
-
}
|
|
57
|
-
.google-oauth-field {
|
|
58
|
-
margin-bottom: 18px;
|
|
59
|
-
}
|
|
60
|
-
.integration-field-label {
|
|
61
|
-
display: block;
|
|
62
|
-
margin-bottom: 8px;
|
|
63
|
-
color: #cdd6f4;
|
|
64
|
-
font-size: 14px;
|
|
65
|
-
font-weight: 600;
|
|
66
|
-
letter-spacing: 0.3px;
|
|
67
|
-
}
|
|
68
|
-
.integration-field-required {
|
|
69
|
-
color: #f38ba8;
|
|
70
|
-
}
|
|
71
|
-
.integration-field-input {
|
|
72
|
-
width: 100%;
|
|
73
|
-
background: rgba(30, 30, 46, 0.6);
|
|
74
|
-
border: 1.5px solid rgba(137, 180, 250, 0.2);
|
|
75
|
-
color: #cdd6f4;
|
|
76
|
-
padding: 11px 14px;
|
|
77
|
-
border-radius: 8px;
|
|
78
|
-
font-size: 13px;
|
|
79
|
-
transition: all 0.2s ease;
|
|
80
|
-
font-weight: 500;
|
|
81
|
-
}
|
|
82
|
-
.integration-field-input:focus {
|
|
83
|
-
outline: none;
|
|
84
|
-
border-color: #89b4fa;
|
|
85
|
-
background: rgba(30, 30, 46, 0.8);
|
|
86
|
-
box-shadow: 0 0 0 3px rgba(137, 180, 250, 0.15);
|
|
87
|
-
}
|
|
88
|
-
.google-oauth-authorize-btn {
|
|
89
|
-
display: inline-block;
|
|
90
|
-
background: linear-gradient(135deg, #89b4fa 0%, #7aa3f0 100%);
|
|
91
|
-
color: #1e1e2e;
|
|
92
|
-
padding: 12px 32px;
|
|
93
|
-
border-radius: 8px;
|
|
94
|
-
text-decoration: none;
|
|
95
|
-
font-weight: 700;
|
|
96
|
-
font-size: 14px;
|
|
97
|
-
margin-bottom: 20px;
|
|
98
|
-
transition: all 0.25s ease;
|
|
99
|
-
border: none;
|
|
100
|
-
cursor: pointer;
|
|
101
|
-
box-shadow: 0 4px 12px rgba(137, 180, 250, 0.25);
|
|
102
|
-
letter-spacing: 0.3px;
|
|
103
|
-
}
|
|
104
|
-
.google-oauth-authorize-btn:hover {
|
|
105
|
-
transform: translateY(-2px);
|
|
106
|
-
box-shadow: 0 6px 16px rgba(137, 180, 250, 0.35);
|
|
107
|
-
background: linear-gradient(135deg, #a8c5ff 0%, #8eb8ff 100%);
|
|
108
|
-
}
|
|
109
|
-
.google-oauth-waiting {
|
|
110
|
-
display: flex;
|
|
111
|
-
align-items: center;
|
|
112
|
-
gap: 12px;
|
|
113
|
-
color: #a6adc8;
|
|
114
|
-
font-size: 13px;
|
|
115
|
-
margin-bottom: 20px;
|
|
116
|
-
background: rgba(137, 180, 250, 0.08);
|
|
117
|
-
padding: 14px 16px;
|
|
118
|
-
border-radius: 8px;
|
|
119
|
-
border-left: 3px solid #89b4fa;
|
|
120
|
-
font-weight: 500;
|
|
121
|
-
}
|
|
122
|
-
.google-oauth-spinner {
|
|
123
|
-
display: inline-block;
|
|
124
|
-
width: 16px;
|
|
125
|
-
height: 16px;
|
|
126
|
-
border: 2.5px solid rgba(137, 180, 250, 0.25);
|
|
127
|
-
border-top-color: #89b4fa;
|
|
128
|
-
border-radius: 50%;
|
|
129
|
-
animation: google-spin 0.8s linear infinite;
|
|
130
|
-
flex-shrink: 0;
|
|
131
|
-
}
|
|
132
|
-
@keyframes google-spin {
|
|
133
|
-
to { transform: rotate(360deg); }
|
|
134
|
-
}
|
|
135
|
-
.google-oauth-connected-info {
|
|
136
|
-
display: flex;
|
|
137
|
-
align-items: center;
|
|
138
|
-
gap: 14px;
|
|
139
|
-
margin-bottom: 20px;
|
|
140
|
-
background: rgba(166, 227, 161, 0.08);
|
|
141
|
-
padding: 16px;
|
|
142
|
-
border-radius: 10px;
|
|
143
|
-
border-left: 3px solid #a6e3a1;
|
|
144
|
-
}
|
|
145
|
-
.google-oauth-connected-badge {
|
|
146
|
-
background: linear-gradient(135deg, rgba(166, 227, 161, 0.3) 0%, rgba(166, 227, 161, 0.15) 100%);
|
|
147
|
-
color: #a6e3a1;
|
|
148
|
-
padding: 6px 14px;
|
|
149
|
-
border-radius: 6px;
|
|
150
|
-
font-size: 12px;
|
|
151
|
-
font-weight: 700;
|
|
152
|
-
letter-spacing: 0.4px;
|
|
153
|
-
text-transform: uppercase;
|
|
154
|
-
border: 1px solid rgba(166, 227, 161, 0.3);
|
|
155
|
-
flex-shrink: 0;
|
|
156
|
-
}
|
|
157
|
-
.google-oauth-email {
|
|
158
|
-
color: #cdd6f4;
|
|
159
|
-
font-size: 14px;
|
|
160
|
-
font-weight: 600;
|
|
161
|
-
letter-spacing: 0.2px;
|
|
162
|
-
}
|
|
163
|
-
.integration-form-actions {
|
|
164
|
-
display: flex;
|
|
165
|
-
gap: 12px;
|
|
166
|
-
justify-content: flex-end;
|
|
167
|
-
margin-top: 24px;
|
|
168
|
-
padding-top: 20px;
|
|
169
|
-
border-top: 1px solid rgba(137, 180, 250, 0.1);
|
|
170
|
-
}
|
|
171
|
-
.integration-btn {
|
|
172
|
-
padding: 10px 24px;
|
|
173
|
-
border-radius: 8px;
|
|
174
|
-
font-weight: 600;
|
|
175
|
-
font-size: 13px;
|
|
176
|
-
border: none;
|
|
177
|
-
cursor: pointer;
|
|
178
|
-
transition: all 0.2s ease;
|
|
179
|
-
letter-spacing: 0.3px;
|
|
180
|
-
}
|
|
181
|
-
.integration-btn.save {
|
|
182
|
-
background: linear-gradient(135deg, #89b4fa 0%, #7aa3f0 100%);
|
|
183
|
-
color: #1e1e2e;
|
|
184
|
-
box-shadow: 0 4px 12px rgba(137, 180, 250, 0.25);
|
|
185
|
-
}
|
|
186
|
-
.integration-btn.save:hover:not(:disabled) {
|
|
187
|
-
transform: translateY(-2px);
|
|
188
|
-
box-shadow: 0 6px 16px rgba(137, 180, 250, 0.35);
|
|
189
|
-
}
|
|
190
|
-
.integration-btn.cancel {
|
|
191
|
-
background: rgba(137, 180, 250, 0.1);
|
|
192
|
-
color: #89b4fa;
|
|
193
|
-
border: 1.5px solid rgba(137, 180, 250, 0.2);
|
|
194
|
-
}
|
|
195
|
-
.integration-btn.cancel:hover:not(:disabled) {
|
|
196
|
-
background: rgba(137, 180, 250, 0.15);
|
|
197
|
-
border-color: #89b4fa;
|
|
198
|
-
}
|
|
199
|
-
.integration-btn.secondary {
|
|
200
|
-
background: rgba(249, 226, 175, 0.1);
|
|
201
|
-
color: #f9e2af;
|
|
202
|
-
border: 1.5px solid rgba(249, 226, 175, 0.25);
|
|
203
|
-
}
|
|
204
|
-
.integration-btn.secondary:hover:not(:disabled) {
|
|
205
|
-
background: rgba(249, 226, 175, 0.18);
|
|
206
|
-
border-color: #f9e2af;
|
|
207
|
-
}
|
|
208
|
-
.integration-btn:disabled {
|
|
209
|
-
opacity: 0.5;
|
|
210
|
-
cursor: not-allowed;
|
|
211
|
-
}
|
|
212
|
-
.google-oauth-shared-banner {
|
|
213
|
-
background: linear-gradient(135deg, rgba(166, 227, 161, 0.15) 0%, rgba(166, 227, 161, 0.05) 100%);
|
|
214
|
-
border: 1px solid rgba(166, 227, 161, 0.3);
|
|
215
|
-
border-left: 3px solid #a6e3a1;
|
|
216
|
-
border-radius: 10px;
|
|
217
|
-
padding: 16px 18px;
|
|
218
|
-
margin-bottom: 8px;
|
|
219
|
-
}
|
|
220
|
-
.google-oauth-shared-title {
|
|
221
|
-
display: flex;
|
|
222
|
-
align-items: center;
|
|
223
|
-
gap: 10px;
|
|
224
|
-
color: #a6e3a1;
|
|
225
|
-
font-size: 14px;
|
|
226
|
-
font-weight: 700;
|
|
227
|
-
letter-spacing: 0.2px;
|
|
228
|
-
margin-bottom: 8px;
|
|
229
|
-
}
|
|
230
|
-
.google-oauth-shared-body {
|
|
231
|
-
color: #a6adc8;
|
|
232
|
-
font-size: 13px;
|
|
233
|
-
line-height: 1.6;
|
|
234
|
-
font-weight: 400;
|
|
235
|
-
}
|
|
236
|
-
`})]})}export{B as GoogleOAuthSetup};
|