diff --git a/desktop/src/main.js b/desktop/src/main.js --- a/desktop/src/main.js +++ b/desktop/src/main.js @@ -22,7 +22,7 @@ } from './push-notifications.js'; const isDev = process.env.ENV === 'dev'; -const url = isDev ? 'http://localhost:3000/comm/' : 'https://web.comm.app'; +const url = isDev ? 'http://localhost:3000/webapp/' : 'https://web.comm.app'; const isMac = process.platform === 'darwin'; const scrollbarCSS = fs.promises.readFile( diff --git a/docs/dev_environment.md b/docs/dev_environment.md --- a/docs/dev_environment.md +++ b/docs/dev_environment.md @@ -366,16 +366,16 @@ ``` mkdir -p keyserver/facts -vim keyserver/facts/commapp_url.json +vim keyserver/facts/webapp_url.json ``` -Your `commapp_url.json` file should look like this: +Your `webapp_url.json` file should look like this: ```json { "baseDomain": "http://localhost:3000", - "basePath": "/comm/", - "baseRoutePath": "/comm/", + "basePath": "/webapp/", + "baseRoutePath": "/webapp/", "https": false, "proxy": "none" } @@ -506,7 +506,7 @@ yarn dev ``` -You should now be able to load the web app in your web browser at http://localhost/comm/. +You should now be able to load the web app in your web browser at http://localhost/webapp/. This command will start two processes. One is `webpack-dev-server`, which will serve the JS files. `webpack-dev-server` also makes sure the website automatically hot-reloads whenever any of the source files change. The other process is `webpack --watch`, which will build the `app.build.cjs` file, as well as rebuilding it whenever any of the source files change. The `app.build.cjs` file is consumed by the Node server in order to pre-render the initial HTML from the web source (“Server-Side Rendering”). @@ -675,7 +675,7 @@ 3. Finally, you should be able to navigate to Profile → Developer tools in the app and set the address of the local server. It should look something like this: ``` - http://w.x.y.z/comm + http://w.x.y.z/webapp ``` Where `w.x.y.z` is the local IP address you found earlier. diff --git a/docs/linux_dev_environment.md b/docs/linux_dev_environment.md --- a/docs/linux_dev_environment.md +++ b/docs/linux_dev_environment.md @@ -91,8 +91,9 @@ <VirtualHost *:80> ProxyRequests on - ProxyPass /comm/ws ws://localhost:3000/ws - ProxyPass /comm/ http://localhost:3000/ + ProxyPass /keyserver/ http://localhost:3000/keyserver/ + ProxyPass /keyserver/ws ws://localhost:3000/keyserver/ws + ProxyPass /webapp/ http://localhost:3000/webapp/ ProxyPass /commlanding/ http://localhost:3000/commlanding/ RequestHeader set "X-Forwarded-Proto" expr=%{REQUEST_SCHEME} diff --git a/docs/nix_keyserver_deployment.md b/docs/nix_keyserver_deployment.md --- a/docs/nix_keyserver_deployment.md +++ b/docs/nix_keyserver_deployment.md @@ -13,7 +13,8 @@ COMM_DATABASE_PASSWORD=<MariaDB password> COMM_JSONCONFIG_secrets_user_credentials='{"username":"<user>","password":"<password>"}' COMM_JSONCONFIG_facts_landing_url='{"baseDomain":"http://localhost","basePath":"/commlanding/","baseRoutePath":"/commlanding/","https":false}' -COMM_JSONCONFIG_facts_commapp_url='{"baseDomain":"http://localhost:3000","basePath":"/comm/","https":false,"baseRoutePath":"/comm/","proxy":"none"}' +COMM_JSONCONFIG_facts_webapp_url='{"baseDomain":"http://localhost:3000","basePath":"/webapp/","https":false,"baseRoutePath":"/webapp/","proxy":"none"}' +COMM_JSONCONFIG_facts_keyserver_url='{"baseDomain":"http://localhost:3000","basePath":"/keyserver/","baseRoutePath":"/keyserver/","https":false,"proxy":"none"}' COMM_JSONCONFIG_facts_webapp_cors='{"domain": "http://localhost:3000"}' # Required to connect to production Identity service @@ -45,7 +46,7 @@ ### URL configuration -- `COMM_JSONCONFIG_facts_commapp_url`: Your keyserver needs to know what its externally-facing URL is in order to construct links. It also needs to know if it’s being proxied to that externally-facing URL, and what the internal route path is. +- `COMM_JSONCONFIG_facts_keyserver_url`: Your keyserver needs to know what its externally-facing URL is in order to construct links. It also needs to know if it’s being proxied to that externally-facing URL, and what the internal route path is. - `baseDomain`: Externally-facing domain. Used for constructing links. - `basePath`: Externally-facing path. Used for constructing links. - `baseRoutePath`: Internally-facing path. Same as basePath if no proxy. If there’s a proxy, this is the local path (e.g. http://localhost:3000/landing would correspond with /landing/) diff --git a/docs/nix_web_workflows.md b/docs/nix_web_workflows.md --- a/docs/nix_web_workflows.md +++ b/docs/nix_web_workflows.md @@ -42,7 +42,7 @@ yarn dev ``` -You should now be able to load the web app in your web browser at http://localhost:3000/comm/. +You should now be able to load the web app in your web browser at http://localhost:3000/webapp/. This command will start two processes. One is `webpack-dev-server`, which will serve the JS files. `webpack-dev-server` also makes sure the website automatically hot-reloads whenever any of the source files change. The other process is `webpack --watch`, which will build the `app.build.cjs` file, as well as rebuilding it whenever any of the source files change. The `app.build.cjs` file is consumed by the Node server in order to pre-render the initial HTML from the web source (“Server-Side Rendering”). diff --git a/keyserver/icons/site.webmanifest b/keyserver/icons/site.webmanifest --- a/keyserver/icons/site.webmanifest +++ b/keyserver/icons/site.webmanifest @@ -3,12 +3,12 @@ "short_name": "SquadCal", "icons": [ { - "src": "/android-chrome-192x192.png", + "src": "android-chrome-192x192.png", "sizes": "192x192", "type": "image/png" }, { - "src": "/android-chrome-512x512.png", + "src": "android-chrome-512x512.png", "sizes": "512x512", "type": "image/png" } diff --git a/keyserver/src/creators/report-creator.js b/keyserver/src/creators/report-creator.js --- a/keyserver/src/creators/report-creator.js +++ b/keyserver/src/creators/report-creator.js @@ -29,7 +29,7 @@ import { handleAsyncPromise } from '../responders/handlers.js'; import { createBotViewer } from '../session/bots.js'; import type { Viewer } from '../session/viewer.js'; -import { getAndAssertCommAppURLFacts } from '../utils/urls.js'; +import { getAndAssertKeyserverURLFacts } from '../utils/urls.js'; const { commbot } = bots; @@ -144,7 +144,7 @@ const { platform, codeVersion } = platformDetails; const platformString = codeVersion ? `${platform} v${codeVersion}` : platform; if (request.type === reportTypes.ERROR) { - const { baseDomain, basePath } = getAndAssertCommAppURLFacts(); + const { baseDomain, basePath } = getAndAssertKeyserverURLFacts(); return ( `${name} got an error :(\n` + `using ${platformString}\n` + diff --git a/keyserver/src/fetchers/upload-fetchers.js b/keyserver/src/fetchers/upload-fetchers.js --- a/keyserver/src/fetchers/upload-fetchers.js +++ b/keyserver/src/fetchers/upload-fetchers.js @@ -18,7 +18,7 @@ import { dbQuery, SQL } from '../database/database.js'; import type { Viewer } from '../session/viewer.js'; -import { getAndAssertCommAppURLFacts } from '../utils/urls.js'; +import { getAndAssertKeyserverURLFacts } from '../utils/urls.js'; type UploadInfo = { content: Buffer, @@ -104,7 +104,7 @@ } function getUploadURL(id: string, secret: string): string { - const { baseDomain, basePath } = getAndAssertCommAppURLFacts(); + const { baseDomain, basePath } = getAndAssertKeyserverURLFacts(); const uploadPath = `${basePath}upload/${id}/${secret}`; if (isDev) { const ipV4 = ip.v4.sync() || 'localhost'; diff --git a/keyserver/src/keyserver.js b/keyserver/src/keyserver.js --- a/keyserver/src/keyserver.js +++ b/keyserver/src/keyserver.js @@ -7,12 +7,14 @@ import cors from 'cors'; import crypto from 'crypto'; import express from 'express'; +import type { $Request, $Response } from 'express'; import expressWs from 'express-ws'; import os from 'os'; import qrcode from 'qrcode'; import './cron/cron.js'; import { qrCodeLinkURL } from 'lib/facts/links.js'; +import { isDev } from 'lib/utils/dev-utils.js'; import { migrate } from './database/migrations.js'; import { jsonEndpoints } from './endpoints.js'; @@ -44,9 +46,9 @@ import { getContentSigningKey } from './utils/olm-utils.js'; import { prefetchAllURLFacts, - getSquadCalURLFacts, + getKeyserverURLFacts, getLandingURLFacts, - getCommAppURLFacts, + getWebAppURLFacts, getWebAppCorsConfig, } from './utils/urls.js'; @@ -60,9 +62,10 @@ initENSCache(), ]); - const squadCalBaseRoutePath = getSquadCalURLFacts()?.baseRoutePath; + const keyserverBaseRoutePath = getKeyserverURLFacts()?.baseRoutePath; const landingBaseRoutePath = getLandingURLFacts()?.baseRoutePath; - const commAppBaseRoutePath = getCommAppURLFacts()?.baseRoutePath; + const webAppURLFacts = getWebAppURLFacts(); + const webAppBaseRoutePath = webAppURLFacts?.baseRoutePath; const compiledFolderOptions = process.env.NODE_ENV === 'development' @@ -233,19 +236,28 @@ server.use(landingBaseRoutePath, landingRouter); } - if (commAppBaseRoutePath) { + if (webAppBaseRoutePath) { const commAppRouter = express.Router(); setupAppRouter(commAppRouter); - server.use(commAppBaseRoutePath, commAppRouter); + server.use(webAppBaseRoutePath, commAppRouter); } - if (squadCalBaseRoutePath) { + if (keyserverBaseRoutePath) { const squadCalRouter = express.Router(); if (keyserverCorsOptions) { squadCalRouter.use(cors(keyserverCorsOptions)); } setupAppRouter(squadCalRouter); - server.use(squadCalBaseRoutePath, squadCalRouter); + server.use(keyserverBaseRoutePath, squadCalRouter); + } + + if (isDev && webAppURLFacts) { + const oldPath = '/comm/'; + server.all(`${oldPath}*`, (req: $Request, res: $Response) => { + const endpoint = req.url.slice(oldPath.length); + const newURL = `${webAppURLFacts.baseDomain}${webAppURLFacts.basePath}${endpoint}`; + res.redirect(newURL); + }); } const listenAddress = (() => { diff --git a/keyserver/src/responders/website-responders.js b/keyserver/src/responders/website-responders.js --- a/keyserver/src/responders/website-responders.js +++ b/keyserver/src/responders/website-responders.js @@ -15,8 +15,9 @@ import { waitForStream } from '../utils/json-stream.js'; import { + getAndAssertKeyserverURLFacts, getAppURLFactsFromRequestURL, - getCommAppURLFacts, + getWebAppURLFacts, } from '../utils/urls.js'; const { renderToNodeStream } = ReactDOMServer; @@ -110,9 +111,18 @@ } } +function stripLastSlash(input: string): string { + return input.replace(/\/$/, ''); +} + async function websiteResponder(req: $Request, res: $Response): Promise<void> { const { basePath } = getAppURLFactsFromRequestURL(req.originalUrl); - const baseURL = basePath.replace(/\/$/, ''); + const baseURL = stripLastSlash(basePath); + + const keyserverURLFacts = getAndAssertKeyserverURLFacts(); + const keyserverURL = `${keyserverURLFacts.baseDomain}${stripLastSlash( + keyserverURLFacts.basePath, + )}`; const loadingPromise = getWebpackCompiledRootComponentForSSR(); @@ -171,6 +181,7 @@ res.end(html` </div> <script> + var keyserverURL = "${keyserverURL}"; var baseURL = "${baseURL}"; var olmFilename = "${olmFilename}"; var commQueryExecutorFilename = "${commQueryExecutorFilename}"; @@ -202,7 +213,7 @@ res.end(); return; } else if (detectionResult.os !== 'iOS') { - const urlFacts = getCommAppURLFacts(); + const urlFacts = getWebAppURLFacts(); const baseDomain = urlFacts?.baseDomain ?? ''; const basePath = urlFacts?.basePath ?? '/'; const redirectUrl = `${baseDomain}${basePath}handle/invite/${secret}`; diff --git a/keyserver/src/utils/urls.js b/keyserver/src/utils/urls.js --- a/keyserver/src/utils/urls.js +++ b/keyserver/src/utils/urls.js @@ -15,8 +15,8 @@ const validProxies = new Set(['apache', 'none']); const sitesObj = Object.freeze({ a: 'landing', - b: 'commapp', - c: 'squadcal', + b: 'webapp', + c: 'keyserver', }); export type Site = $Values<typeof sitesObj>; const sites: $ReadOnlyArray<Site> = values(sitesObj); @@ -46,30 +46,30 @@ await Promise.all(sites.map(fetchURLFacts)); } -function getSquadCalURLFacts(): ?AppURLFacts { - return cachedURLFacts.get('squadcal'); +function getKeyserverURLFacts(): ?AppURLFacts { + return cachedURLFacts.get('keyserver'); } -function getCommAppURLFacts(): ?AppURLFacts { - return cachedURLFacts.get('commapp'); +function getWebAppURLFacts(): ?AppURLFacts { + return cachedURLFacts.get('webapp'); } -function getAndAssertCommAppURLFacts(): AppURLFacts { - const urlFacts = getCommAppURLFacts(); - invariant(urlFacts, 'keyserver/facts/commapp_url.json missing'); +function getAndAssertKeyserverURLFacts(): AppURLFacts { + const urlFacts = getKeyserverURLFacts(); + invariant(urlFacts, 'keyserver/facts/keyserver_url.json missing'); return urlFacts; } // ESLint doesn't recognize that invariant always throws // eslint-disable-next-line consistent-return function getAppURLFactsFromRequestURL(url: string): AppURLFacts { - const commURLFacts = getCommAppURLFacts(); - if (commURLFacts && url.startsWith(commURLFacts.baseRoutePath)) { - return commURLFacts; + const webAppURLFacts = getWebAppURLFacts(); + if (webAppURLFacts && url.startsWith(webAppURLFacts.baseRoutePath)) { + return webAppURLFacts; } - const squadCalURLFacts = getSquadCalURLFacts(); - if (squadCalURLFacts) { - return squadCalURLFacts; + const keyserverURLFacts = getKeyserverURLFacts(); + if (keyserverURLFacts && url.startsWith(keyserverURLFacts.baseRoutePath)) { + return keyserverURLFacts; } invariant(false, 'request received but no URL facts are present'); } @@ -95,9 +95,9 @@ export { prefetchAllURLFacts, - getSquadCalURLFacts, - getCommAppURLFacts, - getAndAssertCommAppURLFacts, + getKeyserverURLFacts, + getWebAppURLFacts, + getAndAssertKeyserverURLFacts, getLandingURLFacts, getAndAssertLandingURLFacts, getAppURLFactsFromRequestURL, diff --git a/native/redux/persist.js b/native/redux/persist.js --- a/native/redux/persist.js +++ b/native/redux/persist.js @@ -93,6 +93,7 @@ import { commCoreModule } from '../native-modules.js'; import { defaultDeviceCameraInfo } from '../types/camera.js'; import { isTaskCancelledError } from '../utils/error-handling.js'; +import { defaultURLPrefix } from '../utils/url-utils.js'; const migrations = { [1]: (state: AppState) => ({ @@ -836,6 +837,22 @@ }, }; }, + [55]: async state => + __DEV__ + ? { + ...state, + keyserverStore: { + ...state.keyserverStore, + keyserverInfos: { + ...state.keyserverStore.keyserverInfos, + [ashoatKeyserverID]: { + ...state.keyserverStore.keyserverInfos[ashoatKeyserverID], + urlPrefix: defaultURLPrefix, + }, + }, + }, + } + : state, }; // After migration 31, we'll no longer want to persist `messageStore.messages` @@ -965,7 +982,7 @@ 'connection', ], debug: __DEV__, - version: 54, + version: 55, transforms: [ messageStoreMessagesBlocklistTransform, reportStoreTransform, diff --git a/native/utils/url-utils.js b/native/utils/url-utils.js --- a/native/utils/url-utils.js +++ b/native/utils/url-utils.js @@ -32,7 +32,7 @@ } function getDevNodeServerURLFromHostname(hostname: string): string { - return `http://${hostname}:3000/comm`; + return `http://${hostname}:3000/keyserver`; } function getDevLandingURLFromHostname(hostname: string): string { diff --git a/web/push-notif/service-worker.js b/web/push-notif/service-worker.js --- a/web/push-notif/service-worker.js +++ b/web/push-notif/service-worker.js @@ -72,7 +72,7 @@ const url = (process.env.NODE_ENV === 'production' ? 'https://web.comm.app' - : 'http://localhost:3000/comm') + `/chat/thread/${threadID}/`; + : 'http://localhost:3000/webapp') + `/chat/thread/${threadID}/`; clients.openWindow(url); } })(), diff --git a/web/redux/default-state.js b/web/redux/default-state.js --- a/web/redux/default-state.js +++ b/web/redux/default-state.js @@ -4,12 +4,13 @@ import { defaultCalendarFilters } from 'lib/types/filter-types.js'; import { defaultConnectionInfo } from 'lib/types/socket-types.js'; import { defaultGlobalThemeInfo } from 'lib/types/theme-types.js'; -import { isDev } from 'lib/utils/dev-utils.js'; import { defaultNotifPermissionAlertInfo } from 'lib/utils/push-alerts.js'; import { ashoatKeyserverID } from 'lib/utils/validation-utils.js'; import type { AppState } from './redux-setup.js'; +declare var keyserverURL: string; + const defaultWebState: AppState = Object.freeze({ navInfo: { activeChatThreadID: null, @@ -79,9 +80,7 @@ [ashoatKeyserverID]: { cookie: null, updatesCurrentAsOf: 0, - urlPrefix: isDev - ? 'http://localhost:3000/comm' - : 'https://web.comm.app', + urlPrefix: keyserverURL, connection: { ...defaultConnectionInfo }, lastCommunicatedPlatformDetails: null, }, diff --git a/web/redux/persist.js b/web/redux/persist.js --- a/web/redux/persist.js +++ b/web/redux/persist.js @@ -38,6 +38,8 @@ import { isSQLiteSupported } from '../database/utils/db-utils.js'; import { workerRequestMessageTypes } from '../types/worker-types.js'; +declare var keyserverURL: string; + const migrations = { [1]: async state => { const { @@ -168,6 +170,19 @@ ...state, globalThemeInfo: defaultGlobalThemeInfo, }), + [9]: async state => ({ + ...state, + keyserverStore: { + ...state.keyserverStore, + keyserverInfos: { + ...state.keyserverStore.keyserverInfos, + [ashoatKeyserverID]: { + ...state.keyserverStore.keyserverInfos[ashoatKeyserverID], + urlPrefix: keyserverURL, + }, + }, + }, + }), }; const persistWhitelist = [ @@ -280,7 +295,7 @@ { debug: isDev }, migrateStorageToSQLite, ): any), - version: 8, + version: 9, transforms: [keyserverStoreTransform], };