`);
const statePromises = {
navInfo: navInfoPromise,
currentUserInfo: currentUserInfoPromise,
sessionID: sessionIDPromise,
serverVerificationResult: serverVerificationResultPromise,
entryStore: entryStorePromise,
threadStore: threadStorePromise,
userStore: userStorePromise,
messageStore: messageStorePromise,
updatesCurrentAsOf: initialTime,
loadingStatuses: {},
calendarFilters: defaultCalendarFilters,
// We can use paths local to the
on web
urlPrefix: '',
windowDimensions: { width: 0, height: 0 },
baseHref,
connection: {
...defaultConnectionInfo('web', viewer.timeZone),
actualizedCalendarQuery: calendarQuery,
},
watchedThreadIDs: [],
lifecycleState: 'active',
nextLocalID: 0,
queuedReports: [],
timeZone: viewer.timeZone,
userAgent: viewer.userAgent,
cookie: undefined,
deviceToken: undefined,
dataLoaded: viewer.loggedIn,
windowActive: true,
};
const stateResult = await promiseAll(statePromises);
const state: AppState = { ...stateResult };
const store: Store
= createStore(reducer, state);
const routerContext = {};
const reactStream = renderToNodeStream(
,
);
if (routerContext.url) {
throw new ServerError('URL modified during server render!');
}
reactStream.pipe(res, { end: false });
await waitForStream(reactStream);
res.write(html`
`);
}
async function handleVerificationRequest(
viewer: Viewer,
code: ?string,
): Promise {
if (!code) {
return null;
}
try {
return await handleCodeVerificationRequest(viewer, code);
} catch (e) {
if (e instanceof ServerError && e.message === 'invalid_code') {
return { success: false };
}
throw e;
}
}
export { websiteResponder };
diff --git a/server/src/server.js b/server/src/server.js
index fe5acbe58..1a4afa414 100644
--- a/server/src/server.js
+++ b/server/src/server.js
@@ -1,95 +1,95 @@
// @flow
import cluster from 'cluster';
import cookieParser from 'cookie-parser';
import express from 'express';
import expressWs from 'express-ws';
import os from 'os';
-import urlFacts from '../facts/url';
import './cron/cron';
import { jsonEndpoints } from './endpoints';
import {
jsonHandler,
downloadHandler,
htmlHandler,
uploadHandler,
} from './responders/handlers';
import { errorReportDownloadResponder } from './responders/report-responders';
import { websiteResponder } from './responders/website-responders';
import { onConnection } from './socket/socket';
import {
multerProcessor,
multimediaUploadResponder,
uploadDownloadResponder,
} from './uploads/uploads';
+import { getGlobalURLFacts } from './utils/urls';
-const { baseRoutePath } = urlFacts;
+const { baseRoutePath } = getGlobalURLFacts();
if (cluster.isMaster) {
const cpuCount = os.cpus().length;
for (let i = 0; i < cpuCount; i++) {
cluster.fork();
}
cluster.on('exit', () => cluster.fork());
} else {
const server = express();
expressWs(server);
server.use(express.json({ limit: '50mb' }));
server.use(cookieParser());
const router = express.Router();
router.use('/images', express.static('images'));
if (process.env.NODE_ENV === 'dev') {
router.use('/fonts', express.static('fonts'));
}
router.use('/misc', express.static('misc'));
router.use(
'/.well-known',
express.static(
'.well-known',
// Necessary for apple-app-site-association file
{
setHeaders: (res) => res.setHeader('Content-Type', 'application/json'),
},
),
);
const compiledFolderOptions =
process.env.NODE_ENV === 'dev'
? undefined
: { maxAge: '1y', immutable: true };
router.use('/compiled', express.static('compiled', compiledFolderOptions));
router.use('/', express.static('icons'));
for (const endpoint in jsonEndpoints) {
// $FlowFixMe Flow thinks endpoint is string
const responder = jsonEndpoints[endpoint];
const expectCookieInvalidation = endpoint === 'log_out';
router.post(
`/${endpoint}`,
jsonHandler(responder, expectCookieInvalidation),
);
}
router.get(
'/download_error_report/:reportID',
downloadHandler(errorReportDownloadResponder),
);
router.get(
'/upload/:uploadID/:secret',
downloadHandler(uploadDownloadResponder),
);
// $FlowFixMe express-ws has side effects that can't be typed
router.ws('/ws', onConnection);
router.get('*', htmlHandler(websiteResponder));
router.post(
'/upload_multimedia',
multerProcessor,
uploadHandler(multimediaUploadResponder),
);
server.use(baseRoutePath, router);
server.listen(parseInt(process.env.PORT, 10) || 3000, 'localhost');
}
diff --git a/server/src/session/cookies.js b/server/src/session/cookies.js
index 09062e86e..564cf65df 100644
--- a/server/src/session/cookies.js
+++ b/server/src/session/cookies.js
@@ -1,807 +1,807 @@
// @flow
import crypto from 'crypto';
import type { $Response, $Request } from 'express';
import invariant from 'invariant';
import bcrypt from 'twin-bcrypt';
import url from 'url';
import { hasMinCodeVersion } from 'lib/shared/version-utils';
import type { Shape } from 'lib/types/core';
import type { Platform, PlatformDetails } from 'lib/types/device-types';
import type { CalendarQuery } from 'lib/types/entry-types';
import {
type ServerSessionChange,
cookieLifetime,
cookieSources,
type CookieSource,
cookieTypes,
sessionIdentifierTypes,
type SessionIdentifierType,
} from 'lib/types/session-types';
import type { InitialClientSocketMessage } from 'lib/types/socket-types';
import type { UserInfo } from 'lib/types/user-types';
import { values } from 'lib/utils/objects';
import { promiseAll } from 'lib/utils/promises';
-import urlFacts from '../../facts/url';
import createIDs from '../creators/id-creator';
import { createSession } from '../creators/session-creator';
import { dbQuery, SQL } from '../database/database';
import { deleteCookie } from '../deleters/cookie-deleters';
import { handleAsyncPromise } from '../responders/handlers';
import { clearDeviceToken } from '../updaters/device-token-updaters';
import { updateThreadMembers } from '../updaters/thread-updaters';
import { assertSecureRequest } from '../utils/security-utils';
+import { getAppURLFacts } from '../utils/urls';
import { Viewer } from './viewer';
import type { AnonymousViewerData, UserViewerData } from './viewer';
-const { baseDomain, basePath, https } = urlFacts;
+const { baseDomain, basePath, https } = getAppURLFacts();
function cookieIsExpired(lastUsed: number) {
return lastUsed + cookieLifetime <= Date.now();
}
type SessionParameterInfo = {|
isSocket: boolean,
sessionID: ?string,
sessionIdentifierType: SessionIdentifierType,
ipAddress: string,
userAgent: ?string,
|};
type FetchViewerResult =
| {| type: 'valid', viewer: Viewer |}
| InvalidFetchViewerResult;
type InvalidFetchViewerResult =
| {|
type: 'nonexistant',
cookieName: ?string,
cookieSource: ?CookieSource,
sessionParameterInfo: SessionParameterInfo,
|}
| {|
type: 'invalidated',
cookieName: string,
cookieID: string,
cookieSource: CookieSource,
sessionParameterInfo: SessionParameterInfo,
platformDetails: ?PlatformDetails,
deviceToken: ?string,
|};
async function fetchUserViewer(
cookie: string,
cookieSource: CookieSource,
sessionParameterInfo: SessionParameterInfo,
): Promise