Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions src/routes/ui/calendar.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import { setAccountCredentials, deleteAccount, getAccountCredentials } from '../../lib/db.js';
import { renderErrorPage } from './shared.js';
import { renderErrorPage, getBaseUrl } from './shared.js';

export function registerRoutes(router, baseUrl) {
export function registerRoutes(router, _baseUrl) {
router.post('/google/setup', (req, res) => {
const { accountName, clientId, clientSecret } = req.body;
if (!accountName || !clientId || !clientSecret) {
return res.status(400).send('Account name, client ID, and secret required');
}
setAccountCredentials('google_calendar', accountName, { clientId, clientSecret });

const redirectUri = `${baseUrl}/ui/google/callback`;
const redirectUri = `${getBaseUrl(req)}/ui/google/callback`;
const scope = 'https://www.googleapis.com/auth/calendar.readonly';
const state = `agentgate_google_${accountName}`;

Expand Down Expand Up @@ -40,7 +40,7 @@ export function registerRoutes(router, baseUrl) {
}

try {
const redirectUri = `${baseUrl}/ui/google/callback`;
const redirectUri = `${getBaseUrl(req)}/ui/google/callback`;

const response = await fetch('https://oauth2.googleapis.com/token', {
method: 'POST',
Expand Down Expand Up @@ -91,7 +91,7 @@ export function registerRoutes(router, baseUrl) {
return res.status(400).send(renderErrorPage('Retry Error', 'Account credentials not found. Please set up the account again.'));
}

const redirectUri = `${baseUrl}/ui/google/callback`;
const redirectUri = `${getBaseUrl(req)}/ui/google/callback`;
const scope = 'https://www.googleapis.com/auth/calendar.readonly';
const state = `agentgate_google_${accountName}`;

Expand Down
10 changes: 5 additions & 5 deletions src/routes/ui/fitbit.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import { setAccountCredentials, deleteAccount, getAccountCredentials } from '../../lib/db.js';
import { renderErrorPage } from './shared.js';
import { renderErrorPage, getBaseUrl } from './shared.js';

export function registerRoutes(router, baseUrl) {
export function registerRoutes(router, _baseUrl) {
router.post('/fitbit/setup', (req, res) => {
const { accountName, clientId, clientSecret } = req.body;
if (!accountName || !clientId || !clientSecret) {
return res.status(400).send('Account name, client ID, and secret required');
}
setAccountCredentials('fitbit', accountName, { clientId, clientSecret });

const redirectUri = `${baseUrl}/ui/fitbit/callback`;
const redirectUri = `${getBaseUrl(req)}/ui/fitbit/callback`;
const scope = 'activity heartrate location nutrition oxygen_saturation profile respiratory_rate settings sleep social temperature weight';
const state = `agentgate_fitbit_${accountName}`;

Expand Down Expand Up @@ -39,7 +39,7 @@ export function registerRoutes(router, baseUrl) {
}

try {
const redirectUri = `${baseUrl}/ui/fitbit/callback`;
const redirectUri = `${getBaseUrl(req)}/ui/fitbit/callback`;
const basicAuth = Buffer.from(`${creds.clientId}:${creds.clientSecret}`).toString('base64');

const response = await fetch('https://api.fitbit.com/oauth2/token', {
Expand Down Expand Up @@ -91,7 +91,7 @@ export function registerRoutes(router, baseUrl) {
return res.status(400).send(renderErrorPage('Retry Error', 'Account credentials not found. Please set up the account again.'));
}

const redirectUri = `${baseUrl}/ui/fitbit/callback`;
const redirectUri = `${getBaseUrl(req)}/ui/fitbit/callback`;
const scope = 'activity heartrate location nutrition oxygen_saturation profile respiratory_rate settings sleep social temperature weight';
const state = `agentgate_fitbit_${accountName}`;

Expand Down
10 changes: 5 additions & 5 deletions src/routes/ui/linkedin.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { setAccountCredentials, deleteAccount, getAccountCredentials } from '../../lib/db.js';
import { renderErrorPage } from './shared.js';
import { renderErrorPage, getBaseUrl } from './shared.js';

export function registerRoutes(router, baseUrl) {
export function registerRoutes(router, _baseUrl) {
const DEFAULT_SCOPES = 'openid profile email w_member_social';

router.post('/linkedin/setup', (req, res) => {
Expand All @@ -15,7 +15,7 @@ export function registerRoutes(router, baseUrl) {

setAccountCredentials('linkedin', accountName, { clientId, clientSecret, scopes: scopeList });

const redirectUri = `${baseUrl}/ui/linkedin/callback`;
const redirectUri = `${getBaseUrl(req)}/ui/linkedin/callback`;
const state = `agentgate_linkedin_${accountName}`;

const authUrl = 'https://www.linkedin.com/oauth/v2/authorization?' +
Expand Down Expand Up @@ -44,7 +44,7 @@ export function registerRoutes(router, baseUrl) {
}

try {
const redirectUri = `${baseUrl}/ui/linkedin/callback`;
const redirectUri = `${getBaseUrl(req)}/ui/linkedin/callback`;

const response = await fetch('https://www.linkedin.com/oauth/v2/accessToken', {
method: 'POST',
Expand Down Expand Up @@ -94,7 +94,7 @@ export function registerRoutes(router, baseUrl) {
return res.status(400).send(renderErrorPage('Retry Error', 'Account credentials not found. Please set up the account again.'));
}

const redirectUri = `${baseUrl}/ui/linkedin/callback`;
const redirectUri = `${getBaseUrl(req)}/ui/linkedin/callback`;
const scopeList = creds.scopes || 'openid profile email w_member_social';
const state = `agentgate_linkedin_${accountName}`;

Expand Down
14 changes: 7 additions & 7 deletions src/routes/ui/mastodon.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { setAccountCredentials, deleteAccount, getAccountCredentials } from '../../lib/db.js';
import { renderErrorPage } from './shared.js';
import { renderErrorPage, getBaseUrl } from './shared.js';

export function registerRoutes(router, baseUrl) {
export function registerRoutes(router, _baseUrl) {
const DEFAULT_SCOPES = 'read write:statuses';

// Simple auth: paste an access token directly (no OAuth dance)
Expand Down Expand Up @@ -35,9 +35,9 @@ export function registerRoutes(router, baseUrl) {
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
client_name: 'agentgate',
redirect_uris: `${baseUrl}/ui/mastodon/callback`,
redirect_uris: `${getBaseUrl(req)}/ui/mastodon/callback`,
scopes: scopeList,
website: baseUrl
website: getBaseUrl(req)
})
});

Expand All @@ -56,7 +56,7 @@ export function registerRoutes(router, baseUrl) {

const authUrl = `https://${cleanInstance}/oauth/authorize?` +
`client_id=${app.client_id}&response_type=code&` +
`redirect_uri=${encodeURIComponent(`${baseUrl}/ui/mastodon/callback`)}&` +
`redirect_uri=${encodeURIComponent(`${getBaseUrl(req)}/ui/mastodon/callback`)}&` +
`scope=${encodeURIComponent(scopeList)}&state=${encodeURIComponent(`agentgate_mastodon_${accountName}`)}`;

res.redirect(authUrl);
Expand Down Expand Up @@ -91,7 +91,7 @@ export function registerRoutes(router, baseUrl) {
body: JSON.stringify({
client_id: creds.clientId,
client_secret: creds.clientSecret,
redirect_uri: `${baseUrl}/ui/mastodon/callback`,
redirect_uri: `${getBaseUrl(req)}/ui/mastodon/callback`,
grant_type: 'authorization_code',
code,
scope: scopeList
Expand Down Expand Up @@ -134,7 +134,7 @@ export function registerRoutes(router, baseUrl) {

const authUrl = `https://${creds.instance}/oauth/authorize?` +
`client_id=${creds.clientId}&response_type=code&` +
`redirect_uri=${encodeURIComponent(`${baseUrl}/ui/mastodon/callback`)}&` +
`redirect_uri=${encodeURIComponent(`${getBaseUrl(req)}/ui/mastodon/callback`)}&` +
`scope=${encodeURIComponent(scopeList)}&state=${encodeURIComponent(`agentgate_mastodon_${accountName}`)}`;

res.redirect(authUrl);
Expand Down
10 changes: 5 additions & 5 deletions src/routes/ui/reddit.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import { setAccountCredentials, deleteAccount, getAccountCredentials } from '../../lib/db.js';
import { renderErrorPage } from './shared.js';
import { renderErrorPage, getBaseUrl } from './shared.js';

export function registerRoutes(router, baseUrl) {
export function registerRoutes(router, _baseUrl) {
router.post('/reddit/setup', (req, res) => {
const { accountName, clientId, clientSecret } = req.body;
if (!accountName || !clientId || !clientSecret) {
return res.status(400).send('Account name, client ID, and secret required');
}
setAccountCredentials('reddit', accountName, { clientId, clientSecret });

const redirectUri = `${baseUrl}/ui/reddit/callback`;
const redirectUri = `${getBaseUrl(req)}/ui/reddit/callback`;
const scope = 'read identity';
const state = `agentgate_reddit_${accountName}`;

Expand Down Expand Up @@ -40,7 +40,7 @@ export function registerRoutes(router, baseUrl) {

try {
const basicAuth = Buffer.from(`${creds.clientId}:${creds.clientSecret}`).toString('base64');
const redirectUri = `${baseUrl}/ui/reddit/callback`;
const redirectUri = `${getBaseUrl(req)}/ui/reddit/callback`;

const response = await fetch('https://www.reddit.com/api/v1/access_token', {
method: 'POST',
Expand Down Expand Up @@ -89,7 +89,7 @@ export function registerRoutes(router, baseUrl) {
return res.status(400).send(renderErrorPage('Retry Error', 'Account credentials not found. Please set up the account again.'));
}

const redirectUri = `${baseUrl}/ui/reddit/callback`;
const redirectUri = `${getBaseUrl(req)}/ui/reddit/callback`;
const scope = 'read identity';
const state = `agentgate_reddit_${accountName}`;

Expand Down
11 changes: 11 additions & 0 deletions src/routes/ui/shared.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,17 @@ export const COOKIE_MAX_AGE = 7 * 24 * 60 * 60 * 1000; // 1 week
export const PORT = process.env.PORT || 3050;
export const BASE_URL = process.env.BASE_URL || `http://localhost:${PORT}`;

/**
* Derive the base URL from an incoming request.
* Priority: BASE_URL env > X-Forwarded-* headers > req.protocol/host
*/
export function getBaseUrl(req) {
if (process.env.BASE_URL) return process.env.BASE_URL;
const proto = req.headers['x-forwarded-proto'] || req.protocol || 'http';
const host = req.headers['x-forwarded-host'] || req.headers.host || `localhost:${PORT}`;
return `${proto}://${host}`;
}

// HTML escape helper
export function escapeHtml(str) {
if (typeof str !== 'string') str = JSON.stringify(str, null, 2);
Expand Down
10 changes: 5 additions & 5 deletions src/routes/ui/youtube.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import { setAccountCredentials, deleteAccount, getAccountCredentials } from '../../lib/db.js';
import { renderErrorPage } from './shared.js';
import { renderErrorPage, getBaseUrl } from './shared.js';

export function registerRoutes(router, baseUrl) {
export function registerRoutes(router, _baseUrl) {
router.post('/youtube/setup', (req, res) => {
const { accountName, clientId, clientSecret } = req.body;
if (!accountName || !clientId || !clientSecret) {
return res.status(400).send('Account name, client ID, and secret required');
}
setAccountCredentials('youtube', accountName, { clientId, clientSecret });

const redirectUri = `${baseUrl}/ui/youtube/callback`;
const redirectUri = `${getBaseUrl(req)}/ui/youtube/callback`;
const scope = 'https://www.googleapis.com/auth/youtube.readonly';
const state = `agentgate_youtube_${accountName}`;

Expand Down Expand Up @@ -39,7 +39,7 @@ export function registerRoutes(router, baseUrl) {
}

try {
const redirectUri = `${baseUrl}/ui/youtube/callback`;
const redirectUri = `${getBaseUrl(req)}/ui/youtube/callback`;

const response = await fetch('https://oauth2.googleapis.com/token', {
method: 'POST',
Expand Down Expand Up @@ -89,7 +89,7 @@ export function registerRoutes(router, baseUrl) {
return res.status(400).send(renderErrorPage('Retry Error', 'Account credentials not found. Please set up the account again.'));
}

const redirectUri = `${baseUrl}/ui/youtube/callback`;
const redirectUri = `${getBaseUrl(req)}/ui/youtube/callback`;
const scope = 'https://www.googleapis.com/auth/youtube.readonly';
const state = `agentgate_youtube_${accountName}`;

Expand Down
Loading