aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/client/apis/GoogleAuthenticationManager.tsx98
-rw-r--r--src/client/views/nodes/TaskBox.tsx57
-rw-r--r--src/server/ApiManagers/GeneralGoogleManager.ts50
-rw-r--r--src/server/apis/google/GoogleApiServerUtils.ts10
-rw-r--r--src/server/apis/google/google_project_credentials.json2
5 files changed, 161 insertions, 56 deletions
diff --git a/src/client/apis/GoogleAuthenticationManager.tsx b/src/client/apis/GoogleAuthenticationManager.tsx
index 1b1d6f734..a93e03e60 100644
--- a/src/client/apis/GoogleAuthenticationManager.tsx
+++ b/src/client/apis/GoogleAuthenticationManager.tsx
@@ -42,46 +42,72 @@ export class GoogleAuthenticationManager extends ObservableReactComponent<object
this.openState && this.resetState(0, 0);
}
- public fetchOrGenerateAccessToken = async (displayIfFound = false) => {
- const response = await Networking.FetchFromServer('/readGoogleAccessToken');
- // if this is an authentication url, activate the UI to register the new access token
- if (new RegExp(AuthenticationUrl).test(response)) {
- this.isOpen = true;
- this.authenticationLink = response;
-
- // GETS STUCK AT THIS PROMISE!!
- return new Promise<string>(resolve => {
- this.disposer?.();
- this.disposer = reaction(
- () => this.authenticationCode,
- async authenticationCode => {
- if (authenticationCode && /\d{1}\/[\w-]{55}/.test(authenticationCode)) {
- resolve(authenticationCode);
- this.disposer?.();
- // const response2 = await Networking.PostToServer('/writeGoogleAccessToken', { authenticationCode });
- // runInAction(() => {
- // this.success = true;
- // this.credentials = response2 as { user_info: { name: string; picture: string }; access_token: string };
- // });
- // resolve((response2 as { access_token: string }).access_token);
- this.resetState();
- }
- }
- );
- });
- }
+ // public fetchOrGenerateAccessToken = async (displayIfFound = false) => {
+ // const response = await Networking.FetchFromServer('/readGoogleAccessToken');
+ // // if this is an authentication url, activate the UI to register the new access token
+
+ // if (new RegExp(AuthenticationUrl).test(response)) {
+ // this.isOpen = true;
+ // this.authenticationLink = response;
- // otherwise, we already have a valid, stored access token and user info
- const response2 = JSON.parse(response) as { user_info: { name: string; picture: string }; access_token: string };
- if (displayIfFound) {
+ // // GETS STUCK AT THIS PROMISE!!
+ // return new Promise<string>(resolve => {
+ // this.disposer?.();
+ // this.disposer = reaction(
+ // () => this.authenticationCode,
+ // async authenticationCode => {
+ // if (authenticationCode && /\d{1}\/[\w-]{55}/.test(authenticationCode)) {
+ // resolve(authenticationCode);
+ // this.disposer?.();
+ // // const response2 = await Networking.PostToServer('/writeGoogleAccessToken', { authenticationCode });
+ // // runInAction(() => {
+ // // this.success = true;
+ // // this.credentials = response2 as { user_info: { name: string; picture: string }; access_token: string };
+ // // });
+ // // resolve((response2 as { access_token: string }).access_token);
+ // this.resetState();
+ // }
+ // }
+ // );
+ // });
+ // }
+
+ // // otherwise, we already have a valid, stored access token and user info
+ // const response2 = JSON.parse(response) as { user_info: { name: string; picture: string }; access_token: string };
+ // if (displayIfFound) {
+ // runInAction(() => {
+ // this.success = true;
+ // this.credentials = response2;
+ // });
+ // this.resetState(-1, -1);
+ // this.isOpen = true;
+ // }
+ // return (response2 as { access_token: string }).access_token;
+ // };
+
+ public fetchOrGenerateAccessToken = async (): Promise<string> => {
+ const response = await Networking.FetchFromServer('/readGoogleAccessToken');
+
+ // This will return a JSON object with { access_token, user_info } if already linked
+ try {
+ const parsed = JSON.parse(response) as { access_token: string; user_info: { name: string; picture: string } };
+
runInAction(() => {
this.success = true;
- this.credentials = response2;
+ this.credentials = parsed;
});
- this.resetState(-1, -1);
- this.isOpen = true;
+
+ return parsed.access_token;
+ } catch (err) {
+ console.warn('Not linked yet or invalid JSON. Redirecting to auth...');
+ // This is an auth URL β€” redirect the user to /refreshGoogle
+ if (typeof response === 'string' && response.startsWith('http')) {
+ window.location.href = response;
+ return ''; // Won’t be used β€” this page will reload anyway
+ }
+
+ throw new Error('Unable to fetch Google access token.');
}
- return (response2 as { access_token: string }).access_token;
};
resetState = action((visibleForMS: number = 3000, fadesOutInMS: number = 500) => {
@@ -132,7 +158,7 @@ export class GoogleAuthenticationManager extends ObservableReactComponent<object
</button>
) : null}
{this.showPasteTargetState ? <input className={'paste-target'} onChange={action(e => (this.authenticationCode = e.currentTarget.value))} placeholder={prompt} /> : null}
- {this.credentials ? (
+ {this.credentials?.user_info?.picture ? (
<>
<img className={'avatar'} src={this.credentials.user_info.picture} />
<span className={'welcome'}>Welcome to Dash, {this.credentials.user_info.name}</span>
diff --git a/src/client/views/nodes/TaskBox.tsx b/src/client/views/nodes/TaskBox.tsx
index 3990356b9..8855e43c8 100644
--- a/src/client/views/nodes/TaskBox.tsx
+++ b/src/client/views/nodes/TaskBox.tsx
@@ -6,6 +6,7 @@ import { DocumentType } from '../../documents/DocumentTypes';
import { FieldView } from './FieldView';
import { DateField } from '../../../fields/DateField';
import { Doc } from '../../../fields/Doc';
+import { Networking } from '../../Network';
import './TaskBox.scss';
import { GoogleAuthenticationManager } from '../../apis/GoogleAuthenticationManager';
@@ -270,7 +271,7 @@ export class TaskBox extends React.Component<TaskBoxProps> {
)}
{/** test button */}
- <button
+ {/* <button
className="task-manager-google"
onClick={async () => {
console.log('GT button clicked');
@@ -301,6 +302,60 @@ export class TaskBox extends React.Component<TaskBoxProps> {
}
}}>
GT
+ </button> */}
+
+ {/* <button
+ className="task-manager-google"
+ onClick={async () => {
+ console.log('GT button clicked');
+ const url = await Networking.FetchFromServer('/readGoogleAccessToken');
+ console.log('πŸ”— Redirecting to Google auth:', url);
+ window.location.href = url; // Redirect user to authenticate
+ }}
+ >
+ GT
+ </button> */}
+
+ <button
+ className="task-manager-google"
+ onClick={async event => {
+ event.preventDefault();
+
+ console.log('GT button clicked');
+ try {
+ const token = await GoogleAuthenticationManager.Instance.fetchOrGenerateAccessToken();
+ console.log('Got token', token);
+
+ const response = await fetch('/googleTasks/create', {
+ method: 'POST',
+ credentials: 'include',
+ headers: {
+ 'Content-Type': 'application/json',
+ Authorization: `Bearer ${token}`,
+ },
+ body: JSON.stringify({
+ title: taskTitle || 'Untitled Task',
+ notes: taskDesc,
+ due: allDay ? String(doc.$task_dateRange)?.split('|')[0] + 'T23:59:00Z' : (doc.$task_endTime instanceof DateField && doc.$task_endTime.date?.toISOString()) || undefined,
+ status: doc.$task_completed ? 'completed' : 'needsAction',
+ completed: doc.$task_completed ? new Date().toISOString() : undefined,
+ }),
+ });
+
+ const result = await response.json();
+ console.log('Google Task result:', result);
+
+ if (result?.id) {
+ alert('βœ… Task sent to Google Tasks!');
+ } else {
+ alert(`❌ Failed: ${result?.error?.message || 'Unknown error'}`);
+ }
+ } catch (err) {
+ console.error('Fetch error:', err);
+ alert('❌ Task creation failed.');
+ }
+ }}>
+ GT
</button>
</div>
);
diff --git a/src/server/ApiManagers/GeneralGoogleManager.ts b/src/server/ApiManagers/GeneralGoogleManager.ts
index e8debfc12..25589ccb5 100644
--- a/src/server/ApiManagers/GeneralGoogleManager.ts
+++ b/src/server/ApiManagers/GeneralGoogleManager.ts
@@ -71,24 +71,20 @@ export default class GeneralGoogleManager extends ApiManager {
subscription: new RouteSubscriber('googleTasks').add('create'),
secureHandler: async ({ req, res, user }) => {
try {
- const { credentials } = await GoogleApiServerUtils.retrieveCredentials(user.id);
- const access_token = user.googleToken || credentials?.access_token; // if googleToken expires, we need to renew it.
-
- if (!access_token) {
- return res.status(401).send('Google access token not found.');
+ const auth = await GoogleApiServerUtils.retrieveOAuthClient(user);
+
+ if (!auth) {
+ return res.status(401).send('Google credentials missing or invalid.');
}
-
- const auth = new google.auth.OAuth2();
- auth.setCredentials({ access_token: access_token });
-
+
const tasks = google.tasks({ version: 'v1', auth });
-
- const { title, notes, due } = req.body;
+
+ const { title, notes, due, status, completed } = req.body;
const result = await tasks.tasks.insert({
tasklist: '@default',
- requestBody: { title, notes, due },
+ requestBody: { title, notes, due, status, completed},
});
-
+
res.status(200).send(result.data);
} catch (err) {
console.error('Google Tasks error:', err);
@@ -102,9 +98,31 @@ export default class GeneralGoogleManager extends ApiManager {
subscription: '/refreshGoogle',
secureHandler: async ({ user, req, res }) => {
const code = req.query.code as string;
- _success(res, code);
- user.googleToken = code;
- user.save();
+ console.log('/refreshGoogle hit with code:', code);
+
+ try {
+ const enriched = await GoogleApiServerUtils.processNewUser(user.id, code);
+
+ if (enriched.refresh_token) {
+ console.log('Enriched credentials:', enriched);
+
+ if (enriched.refresh_token) {
+ user.googleToken = enriched.refresh_token;
+ await user.save();
+ console.log('Saved refresh token to user model');
+ } else {
+ console.warn('No refresh token returned');
+ }
+ }
+
+ // await user.save();
+ // _success(res, 'Google account successfully linked!');
+ res.redirect('/home');
+
+ } catch (err) {
+ console.error('Failed to process Google code:', err);
+ res.status(500).send('Error linking Google account');
+ }
// const response2 = await Networking.PostToServer('/writeGoogleAccessToken', { authenticationCode });
// runInAction(() => {
diff --git a/src/server/apis/google/GoogleApiServerUtils.ts b/src/server/apis/google/GoogleApiServerUtils.ts
index 75f904331..56bc79119 100644
--- a/src/server/apis/google/GoogleApiServerUtils.ts
+++ b/src/server/apis/google/GoogleApiServerUtils.ts
@@ -59,6 +59,7 @@ export namespace GoogleApiServerUtils {
*/
export function processProjectCredentials(): void {
const { client_secret: clientSecret, client_id: clientId, redirect_uris: redirectUris } = GoogleCredentialsLoader.ProjectCredentials;
+ console.log('Loaded Google redirect URIs:', redirectUris);
// initialize the global authorization client
oAuthOptions = {
clientId,
@@ -191,7 +192,12 @@ export namespace GoogleApiServerUtils {
return oauth2Client.generateAuthUrl({
access_type: 'offline',
- scope: ['https://www.googleapis.com/auth/tasks'],
+ scope: [
+ 'https://www.googleapis.com/auth/tasks',
+ 'openid',
+ 'profile'
+ ],
+ prompt: 'consent', // This ensures we get a refresh token
});
}
@@ -306,7 +312,7 @@ export namespace GoogleApiServerUtils {
const headerParameters = { headers: { 'Content-Type': 'application/x-www-form-urlencoded' } };
const { client_id, client_secret } = GoogleCredentialsLoader.ProjectCredentials;
const params = new URLSearchParams({
- refresh_token: credentials.refresh_token!, // AARAV use user.googleToken
+ refresh_token: credentials.refresh_token!, // AARAV use user.googleToken
client_id,
client_secret,
grant_type: 'refresh_token',
diff --git a/src/server/apis/google/google_project_credentials.json b/src/server/apis/google/google_project_credentials.json
index 738e13647..010f9a626 100644
--- a/src/server/apis/google/google_project_credentials.json
+++ b/src/server/apis/google/google_project_credentials.json
@@ -6,6 +6,6 @@
"token_uri": "https://oauth2.googleapis.com/token",
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_secret": "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",
- "redirect_uris": ["urn:ietf:wg:oauth:2.0:oob", "http://localhost"]
+ "redirect_uris": ["http://localhost:1050/refreshGoogle"]
}
} \ No newline at end of file