2024-01-16 14:38:05 +00:00
|
|
|
import glob from 'fast-glob'
|
|
|
|
import { mkdirSync, writeFileSync } from 'fs'
|
|
|
|
import { exec } from '../../../scripts/lib/exec'
|
|
|
|
import { Config } from './vercel-output-config'
|
|
|
|
|
|
|
|
import { config } from 'dotenv'
|
2024-02-26 12:30:35 +00:00
|
|
|
import json5 from 'json5'
|
2024-01-16 14:38:05 +00:00
|
|
|
import { nicelog } from '../../../scripts/lib/nicelog'
|
2024-02-26 12:30:35 +00:00
|
|
|
|
|
|
|
import { T } from '@tldraw/validate'
|
Readonly / room creation omnibus (#3192)
Reworks how the readonly urls work. Till now we just used a simple
function that would scramble the slugs. Now we use a proper key value
mapping between regular and readonly slugs:
- We use two KV stores. One is for going from a slug to a readonly slug
and the other one for going the other way around. They are populated at
the same time.
- We separate preview KV stores (dev, preview, staging) from production
one. I've already created these on Cloudflare. [My understanding is
](https://developers.cloudflare.com/kv/reference/data-security/#encryption-at-rest)that
ids [can be
public](https://community.cloudflare.com/t/is-it-safe-to-keep-kv-ids-in-a-public-git-repo/517387/4)
since we can only access KV from our worker. Happy to move them to env
variables though.
- [x] Disable creating new rooms when tldraw is embedded inside iframes
on other websites (we check the referrer and if it's not the same as the
iframe's origin we don't allow it)
- [x] Fork a project when inside an iframe now opens the forked project
on tldraw.com and not inside iframe.
- [x] We allow embeding of iframes, but we now track the where they are
used via the referrer. We send this to Vercel analytics.
- [x] Improved UX of the share menu to make it less confusing. Toggle is
gone.
- [x] `/new` and `/r` routes not redirect to `/`.
- [x] This introduces a new `/ro` route for readonly rooms. Legacy rooms
still live on `/v`.
- [x] Brought back `dotcom-shared` project to share code between BE and
FE. Mostly types.
- [x] Prevent creating of rooms by entering `/r/non-existing-slug`.
- [x] Handle getting a readonly slug for old rooms. Added a comment
about it
[here](https://github.com/tldraw/tldraw/pull/3192/files#diff-c0954b3dc71bb7097c39656441175f3238ed60cf5cee64077c06e21da82182cbR17-R18).
- [x] We no longer expose editor on the window object for readonly
rooms. Prevents the users disabling readonly rooms manually.
### Change Type
<!-- ❗ Please select a 'Scope' label ❗️ -->
- [ ] `sdk` — Changes the tldraw SDK
- [x] `dotcom` — Changes the tldraw.com web app
- [ ] `docs` — Changes to the documentation, examples, or templates.
- [ ] `vs code` — Changes to the vscode plugin
- [ ] `internal` — Does not affect user-facing stuff
<!-- ❗ Please select a 'Type' label ❗️ -->
- [ ] `bugfix` — Bug fix
- [ ] `feature` — New feature
- [x] `improvement` — Improving existing features
- [ ] `chore` — Updating dependencies, other boring stuff
- [ ] `galaxy brain` — Architectural changes
- [ ] `tests` — Changes to any test code
- [ ] `tools` — Changes to infrastructure, CI, internal scripts,
debugging tools, etc.
- [ ] `dunno` — I don't know
### Test Plan
1. Make sure old readonly rooms still work.
2. Creating a readonly link from an existing room should still use `/v`
path.
3. Newly created rooms should use `/ro` path for readonly rooms. Make
sure these work as well.
4. `/r` room was disabled and redirects to `/`
5. `/new` should still work when not inside iframes.
- [x] Unit Tests
- [ ] End to end tests
### Release Notes
1. This adds new functionality for readonly rooms:
- We have a new route `/ro` for newly created readonly rooms. These
rooms no longer use the scrambling logic to create readonly slugs.
Instead we now use KV storage from cloudflare to track the mapping for
slugs -> readonly slug and readonly slug -> slug.
- The old route `/v` is preserved, so that the old room still work as
they did before.
- For old rooms we will keep on generating the old readonly slugs, but
for new rooms we'll start using the new logic.
2. We no longer prevent embedding of tldraw inside iframes.
3. We do prevent generating new rooms from inside the iframes though.
`/r`, `/new`, `/r/non-existing-id` should not allow creation of new
rooms inside iframes. Only `/new` still works when not inside iframes.
4. Forking a project from inside an iframe now opens it on tldraw.com
5. Slight copy change on the sharing menu. We no longer have a toggle
between readonly and non-readonly links.
6. `editor` and `app` are no longer exposed on the window object for
readonly rooms. Prevents users from using the `updateInstanceState` to
escape readonly rooms.
---------
Co-authored-by: Mime Čuvalo <mimecuvalo@gmail.com>
2024-04-25 14:10:40 +00:00
|
|
|
import { getMultiplayerServerURL } from '../vite.config'
|
2024-02-26 12:30:35 +00:00
|
|
|
|
|
|
|
// We load the list of routes that should be forwarded to our SPA's index.html here.
|
|
|
|
// It uses a jest snapshot file because deriving the set of routes from our
|
|
|
|
// react-router config works fine in our test environment, but is tricky to get running in this
|
|
|
|
// build script environment for various reasons (no global React, tsx being weird about decorators, etc).
|
|
|
|
function loadSpaRoutes() {
|
|
|
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
|
|
const routesJson = require('../src/__snapshots__/routes.test.tsx.snap')['the_routes 1']
|
|
|
|
const routes = T.arrayOf(
|
|
|
|
T.object({
|
|
|
|
reactRouterPattern: T.string,
|
|
|
|
vercelRouterPattern: T.string,
|
|
|
|
})
|
|
|
|
).validate(json5.parse(routesJson))
|
|
|
|
return routes.map((route) => ({
|
|
|
|
check: true,
|
|
|
|
src: route.vercelRouterPattern,
|
|
|
|
dest: '/index.html',
|
|
|
|
}))
|
|
|
|
}
|
Make Vercel URL rewrites precise (#2913)
### The problem
Right now we use a catchall path in Vercel routing config to rewrite all
requests that don't match existing assets to `/index.html`, which is
needed for client side routing to work. This, however, messes up 404
errors for truly non-existing files which won't be handled by the SPA,
because they get redirected to index.html.
Even worse, this interacts very poorly with caching. Normally if we
request a non-existent file, then put the file in place, and request the
file again, we'll get 404 the first time and the actual file the second
time. However, in our case we instead return `/index.html` after the
first attempt and cache that response, making it impossible to correct a
missing file without cache flush.
### The solution
One way to fix this is to make the regex in Vercel config precise, so
that they only match our SPA routes. However, it can be dangerous,
because this means we'll need to manually update the config with new SPA
routes every time we add any. This PR tests that regexes we're using in
Vercel match all routes that we set in the SPA router.
### Potential future improvements
It's very possible to generate Vercel's config from React Router routing
objects, but at the moment it's not done because that would require
importing most of dotcom during the build phase, which seem to cause
errors.
### Change Type
- [x] `internal` — Any other changes that don't affect the published
package[^2]
### Test Plan
1. Might need a light smoke test after deployment to dotcom.
- [x] End to end tests
2024-02-22 18:25:45 +00:00
|
|
|
|
2024-01-16 14:38:05 +00:00
|
|
|
config({
|
|
|
|
path: './.env.local',
|
|
|
|
})
|
|
|
|
|
|
|
|
nicelog('The multiplayer server is', process.env.MULTIPLAYER_SERVER)
|
|
|
|
|
|
|
|
async function build() {
|
2024-02-26 12:30:35 +00:00
|
|
|
// make sure we have the latest routes
|
|
|
|
await exec('yarn', ['test', 'src/routes.test.tsx'])
|
|
|
|
const spaRoutes = loadSpaRoutes()
|
2024-01-16 14:38:05 +00:00
|
|
|
await exec('vite', ['build', '--emptyOutDir'])
|
|
|
|
await exec('yarn', ['run', '-T', 'sentry-cli', 'sourcemaps', 'inject', 'dist/assets'])
|
|
|
|
// Clear output static folder (in case we are running locally and have already built the app once before)
|
|
|
|
await exec('rm', ['-rf', '.vercel/output'])
|
|
|
|
mkdirSync('.vercel/output', { recursive: true })
|
|
|
|
await exec('cp', ['-r', 'dist', '.vercel/output/static'])
|
|
|
|
await exec('rm', ['-rf', ...glob.sync('.vercel/output/static/**/*.js.map')])
|
Make Vercel URL rewrites precise (#2913)
### The problem
Right now we use a catchall path in Vercel routing config to rewrite all
requests that don't match existing assets to `/index.html`, which is
needed for client side routing to work. This, however, messes up 404
errors for truly non-existing files which won't be handled by the SPA,
because they get redirected to index.html.
Even worse, this interacts very poorly with caching. Normally if we
request a non-existent file, then put the file in place, and request the
file again, we'll get 404 the first time and the actual file the second
time. However, in our case we instead return `/index.html` after the
first attempt and cache that response, making it impossible to correct a
missing file without cache flush.
### The solution
One way to fix this is to make the regex in Vercel config precise, so
that they only match our SPA routes. However, it can be dangerous,
because this means we'll need to manually update the config with new SPA
routes every time we add any. This PR tests that regexes we're using in
Vercel match all routes that we set in the SPA router.
### Potential future improvements
It's very possible to generate Vercel's config from React Router routing
objects, but at the moment it's not done because that would require
importing most of dotcom during the build phase, which seem to cause
errors.
### Change Type
- [x] `internal` — Any other changes that don't affect the published
package[^2]
### Test Plan
1. Might need a light smoke test after deployment to dotcom.
- [x] End to end tests
2024-02-22 18:25:45 +00:00
|
|
|
|
2024-01-16 14:38:05 +00:00
|
|
|
writeFileSync(
|
|
|
|
'.vercel/output/config.json',
|
|
|
|
JSON.stringify(
|
|
|
|
{
|
|
|
|
version: 3,
|
|
|
|
routes: [
|
|
|
|
// rewrite api calls to the multiplayer server
|
|
|
|
{
|
|
|
|
src: '^/api(/(.*))?$',
|
Readonly / room creation omnibus (#3192)
Reworks how the readonly urls work. Till now we just used a simple
function that would scramble the slugs. Now we use a proper key value
mapping between regular and readonly slugs:
- We use two KV stores. One is for going from a slug to a readonly slug
and the other one for going the other way around. They are populated at
the same time.
- We separate preview KV stores (dev, preview, staging) from production
one. I've already created these on Cloudflare. [My understanding is
](https://developers.cloudflare.com/kv/reference/data-security/#encryption-at-rest)that
ids [can be
public](https://community.cloudflare.com/t/is-it-safe-to-keep-kv-ids-in-a-public-git-repo/517387/4)
since we can only access KV from our worker. Happy to move them to env
variables though.
- [x] Disable creating new rooms when tldraw is embedded inside iframes
on other websites (we check the referrer and if it's not the same as the
iframe's origin we don't allow it)
- [x] Fork a project when inside an iframe now opens the forked project
on tldraw.com and not inside iframe.
- [x] We allow embeding of iframes, but we now track the where they are
used via the referrer. We send this to Vercel analytics.
- [x] Improved UX of the share menu to make it less confusing. Toggle is
gone.
- [x] `/new` and `/r` routes not redirect to `/`.
- [x] This introduces a new `/ro` route for readonly rooms. Legacy rooms
still live on `/v`.
- [x] Brought back `dotcom-shared` project to share code between BE and
FE. Mostly types.
- [x] Prevent creating of rooms by entering `/r/non-existing-slug`.
- [x] Handle getting a readonly slug for old rooms. Added a comment
about it
[here](https://github.com/tldraw/tldraw/pull/3192/files#diff-c0954b3dc71bb7097c39656441175f3238ed60cf5cee64077c06e21da82182cbR17-R18).
- [x] We no longer expose editor on the window object for readonly
rooms. Prevents the users disabling readonly rooms manually.
### Change Type
<!-- ❗ Please select a 'Scope' label ❗️ -->
- [ ] `sdk` — Changes the tldraw SDK
- [x] `dotcom` — Changes the tldraw.com web app
- [ ] `docs` — Changes to the documentation, examples, or templates.
- [ ] `vs code` — Changes to the vscode plugin
- [ ] `internal` — Does not affect user-facing stuff
<!-- ❗ Please select a 'Type' label ❗️ -->
- [ ] `bugfix` — Bug fix
- [ ] `feature` — New feature
- [x] `improvement` — Improving existing features
- [ ] `chore` — Updating dependencies, other boring stuff
- [ ] `galaxy brain` — Architectural changes
- [ ] `tests` — Changes to any test code
- [ ] `tools` — Changes to infrastructure, CI, internal scripts,
debugging tools, etc.
- [ ] `dunno` — I don't know
### Test Plan
1. Make sure old readonly rooms still work.
2. Creating a readonly link from an existing room should still use `/v`
path.
3. Newly created rooms should use `/ro` path for readonly rooms. Make
sure these work as well.
4. `/r` room was disabled and redirects to `/`
5. `/new` should still work when not inside iframes.
- [x] Unit Tests
- [ ] End to end tests
### Release Notes
1. This adds new functionality for readonly rooms:
- We have a new route `/ro` for newly created readonly rooms. These
rooms no longer use the scrambling logic to create readonly slugs.
Instead we now use KV storage from cloudflare to track the mapping for
slugs -> readonly slug and readonly slug -> slug.
- The old route `/v` is preserved, so that the old room still work as
they did before.
- For old rooms we will keep on generating the old readonly slugs, but
for new rooms we'll start using the new logic.
2. We no longer prevent embedding of tldraw inside iframes.
3. We do prevent generating new rooms from inside the iframes though.
`/r`, `/new`, `/r/non-existing-id` should not allow creation of new
rooms inside iframes. Only `/new` still works when not inside iframes.
4. Forking a project from inside an iframe now opens it on tldraw.com
5. Slight copy change on the sharing menu. We no longer have a toggle
between readonly and non-readonly links.
6. `editor` and `app` are no longer exposed on the window object for
readonly rooms. Prevents users from using the `updateInstanceState` to
escape readonly rooms.
---------
Co-authored-by: Mime Čuvalo <mimecuvalo@gmail.com>
2024-04-25 14:10:40 +00:00
|
|
|
dest: `${getMultiplayerServerURL()}$1`,
|
2024-01-16 14:38:05 +00:00
|
|
|
check: true,
|
|
|
|
},
|
|
|
|
// cache static assets immutably
|
|
|
|
{
|
|
|
|
src: '^/assets/(.*)$',
|
|
|
|
headers: { 'Cache-Control': 'public, max-age=31536000, immutable' },
|
|
|
|
},
|
|
|
|
// serve static files
|
|
|
|
{
|
2024-02-06 13:31:02 +00:00
|
|
|
handle: 'miss',
|
2024-01-16 14:38:05 +00:00
|
|
|
},
|
|
|
|
// finally handle SPA routing
|
Make Vercel URL rewrites precise (#2913)
### The problem
Right now we use a catchall path in Vercel routing config to rewrite all
requests that don't match existing assets to `/index.html`, which is
needed for client side routing to work. This, however, messes up 404
errors for truly non-existing files which won't be handled by the SPA,
because they get redirected to index.html.
Even worse, this interacts very poorly with caching. Normally if we
request a non-existent file, then put the file in place, and request the
file again, we'll get 404 the first time and the actual file the second
time. However, in our case we instead return `/index.html` after the
first attempt and cache that response, making it impossible to correct a
missing file without cache flush.
### The solution
One way to fix this is to make the regex in Vercel config precise, so
that they only match our SPA routes. However, it can be dangerous,
because this means we'll need to manually update the config with new SPA
routes every time we add any. This PR tests that regexes we're using in
Vercel match all routes that we set in the SPA router.
### Potential future improvements
It's very possible to generate Vercel's config from React Router routing
objects, but at the moment it's not done because that would require
importing most of dotcom during the build phase, which seem to cause
errors.
### Change Type
- [x] `internal` — Any other changes that don't affect the published
package[^2]
### Test Plan
1. Might need a light smoke test after deployment to dotcom.
- [x] End to end tests
2024-02-22 18:25:45 +00:00
|
|
|
...spaRoutes,
|
|
|
|
// react router will handle drawing the 404 page
|
2024-01-16 14:38:05 +00:00
|
|
|
{
|
2024-02-06 13:31:02 +00:00
|
|
|
check: true,
|
2024-01-16 14:38:05 +00:00
|
|
|
src: '.*',
|
|
|
|
dest: '/index.html',
|
Make Vercel URL rewrites precise (#2913)
### The problem
Right now we use a catchall path in Vercel routing config to rewrite all
requests that don't match existing assets to `/index.html`, which is
needed for client side routing to work. This, however, messes up 404
errors for truly non-existing files which won't be handled by the SPA,
because they get redirected to index.html.
Even worse, this interacts very poorly with caching. Normally if we
request a non-existent file, then put the file in place, and request the
file again, we'll get 404 the first time and the actual file the second
time. However, in our case we instead return `/index.html` after the
first attempt and cache that response, making it impossible to correct a
missing file without cache flush.
### The solution
One way to fix this is to make the regex in Vercel config precise, so
that they only match our SPA routes. However, it can be dangerous,
because this means we'll need to manually update the config with new SPA
routes every time we add any. This PR tests that regexes we're using in
Vercel match all routes that we set in the SPA router.
### Potential future improvements
It's very possible to generate Vercel's config from React Router routing
objects, but at the moment it's not done because that would require
importing most of dotcom during the build phase, which seem to cause
errors.
### Change Type
- [x] `internal` — Any other changes that don't affect the published
package[^2]
### Test Plan
1. Might need a light smoke test after deployment to dotcom.
- [x] End to end tests
2024-02-22 18:25:45 +00:00
|
|
|
status: 404,
|
2024-01-16 14:38:05 +00:00
|
|
|
},
|
|
|
|
],
|
|
|
|
overrides: {},
|
|
|
|
} satisfies Config,
|
|
|
|
null,
|
|
|
|
2
|
|
|
|
)
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
build()
|