diff --git a/apps/api/.env.development b/apps/api/.env.development new file mode 100644 index 000000000..49b79c58a --- /dev/null +++ b/apps/api/.env.development @@ -0,0 +1,21 @@ +ENVIRONMENT="development" + +AUTH0_DOMAIN="getjetstream-dev.us.auth0.com" +AUTH0_M2M_DOMAIN="getjetstream-dev.us.auth0.com" + +CONTENTFUL_HOST="https://api.contentful.com" + +GOOGLE_REDIRECT_URI="http://localhost:3333/oauth/google/callback" + +HONEYCOMB_ENABLED=false + +JETSTREAM_CLIENT_URL="http://localhost:4200/app" +JETSTREAM_SERVER_DOMAIN="localhost:3333" +JETSTREAM_SERVER_URL="http://localhost:3333" + +NX_AUTH_AUDIENCE="http://getjetstream.app/app_metadata" +NX_BRANCH="main" +NX_SFDC_API_VERSION="60.0" + +SFDC_API_VERSION="60.0" +SFDC_CALLBACK_URL="http://localhost:3333/oauth/sfdc/callback" diff --git a/apps/api/.env.production b/apps/api/.env.production new file mode 100644 index 000000000..19a14262a --- /dev/null +++ b/apps/api/.env.production @@ -0,0 +1,21 @@ +ENVIRONMENT="production" + +AUTH0_DOMAIN="auth.getjetstream.app" +AUTH0_M2M_DOMAIN="getjetstream.us.auth0.com" + +CONTENTFUL_HOST="cdn.contentful.com" + +GOOGLE_REDIRECT_URI="https://getjetstream.app/oauth/google/callback" + +HONEYCOMB_ENABLED=true + +JETSTREAM_CLIENT_URL="https://getjetstream.app/app" +JETSTREAM_SERVER_DOMAIN="getjetstream.app" +JETSTREAM_SERVER_URL="https://getjetstream.app" + +NX_AUTH_AUDIENCE="http://getjetstream.app/app_metadata" +NX_BRANCH="main" +NX_SFDC_API_VERSION="59.0" + +SFDC_API_VERSION="59.0" +SFDC_CALLBACK_URL="https://getjetstream.app/oauth/sfdc/callback" diff --git a/apps/api/src/app/controllers/sf-bulk-api.controller.ts b/apps/api/src/app/controllers/sf-bulk-api.controller.ts index 47ed64375..15cd05b33 100644 --- a/apps/api/src/app/controllers/sf-bulk-api.controller.ts +++ b/apps/api/src/app/controllers/sf-bulk-api.controller.ts @@ -186,7 +186,6 @@ export async function downloadResults(req: Request, res: Response, next: NextFun let isFirstChunk = true; csvParseStream.on('data', (data) => { - console.log('DATA: %o', data); data = JSON.stringify(data); if (isFirstChunk) { isFirstChunk = false; @@ -197,16 +196,21 @@ export async function downloadResults(req: Request, res: Response, next: NextFun res.write(data); }); csvParseStream.on('finish', () => { - console.log('FINISH'); res.write(']}'); - res.status(200).send(); + if (!res.headersSent) { + res.status(200).send(); + } else { + logger.warn('Response headers already sent. csvParseStream[finish]', { requestId: res.locals.requestId }); + } }); csvParseStream.on('error', (err) => { logger.warn('Error streaming files from Salesforce. %o', err, { requestId: res.locals.requestId }); - res.status(400).send(); + if (!res.headersSent) { + res.status(400).send(); + } else { + logger.warn('Response headers already sent. csvParseStream[error]', { requestId: res.locals.requestId }); + } }); - - // csvParseStream.pipe(res); } catch (ex) { next(new UserFacingError(ex.message)); } diff --git a/apps/api/src/app/controllers/sf-metadata-tooling.controller.ts b/apps/api/src/app/controllers/sf-metadata-tooling.controller.ts index 308efd54f..9161b209f 100644 --- a/apps/api/src/app/controllers/sf-metadata-tooling.controller.ts +++ b/apps/api/src/app/controllers/sf-metadata-tooling.controller.ts @@ -418,8 +418,9 @@ export async function anonymousApex(req: Request, res: Response, next: NextFunct }, }; sendJson(res, results); + } else { + next(new UserFacingError(response.errorMessage)); } - next(new UserFacingError(response.errorMessage)); } catch (ex) { next(ex); } diff --git a/apps/api/src/app/utils/response.handlers.ts b/apps/api/src/app/utils/response.handlers.ts index c4d5cd408..1bdeb5ef6 100644 --- a/apps/api/src/app/utils/response.handlers.ts +++ b/apps/api/src/app/utils/response.handlers.ts @@ -5,11 +5,35 @@ import * as express from 'express'; import * as salesforceOrgsDb from '../db/salesforce-org.db'; import { AuthenticationError, NotFoundError, UserFacingError } from './error-handler'; -export function healthCheck(req: express.Request, res: express.Response) { - return res.status(200).end(); +export async function healthCheck(req: express.Request, res: express.Response) { + try { + await this.prismaService.$queryRaw`SELECT 1`; + res.status(200).json({ + error: false, + uptime: process.uptime(), + message: 'Healthy', + }); + } catch (ex) { + res.status(500).json({ + error: true, + uptime: process.uptime(), + message: `Unhealthy: ${ex.message}`, + }); + } } export function sendJson(res: express.Response, content?: ResponseType, status = 200) { + if (res.headersSent) { + logger.warn('Response headers already sent', { requestId: res.locals.requestId }); + try { + rollbarServer.warn('Response not handled by sendJson, headers already sent', new Error('headers already sent'), { + requestId: res.locals.requestId, + }); + } catch (ex) { + logger.error('Error sending to Rollbar', ex, { requestId: res.locals.requestId }); + } + return; + } res.status(status); return res.json({ data: content || {} }); } @@ -45,6 +69,16 @@ export async function uncaughtErrorHandler(err: any, req: express.Request, res: ...userInfo, }); + if (res.headersSent) { + logger.warn('Response headers already sent', { requestId: res.locals.requestId }); + try { + rollbarServer.warn('Error not handled by error handler, headers already sent', req, userInfo, err, new Error('headers already sent')); + } catch (ex) { + logger.error('Error sending to Rollbar', ex, { requestId: res.locals.requestId }); + } + return; + } + const isJson = (req.get(HTTP.HEADERS.ACCEPT) || '').includes(HTTP.CONTENT_TYPE.JSON); // If org had a connection error, ensure that the database is updated diff --git a/apps/api/src/main.ts b/apps/api/src/main.ts index 4f300bb90..78ff514c5 100644 --- a/apps/api/src/main.ts +++ b/apps/api/src/main.ts @@ -320,7 +320,7 @@ if (ENV.NODE_ENV === 'production' && cluster.isPrimary) { if (environment.production || ENV.IS_CI) { app.use(express.static(join(__dirname, '../jetstream'))); - app.use('/app', logRoute, (req: express.Request, res: express.Response) => { + app.use('/app', (req: express.Request, res: express.Response) => { res.sendFile(join(__dirname, '../jetstream/index.html')); }); } diff --git a/package.json b/package.json index 6b1aea06d..b71e1777f 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,8 @@ "version": "3.10.3", "license": "GNU Lesser General Public License v3.0", "engines": { - "node": "20" + "node": "20", + "yarn": "1.22.21" }, "scripts": { "init:project": "ts-node ./scripts/init-project.ts",