Skip to content

Commit 5b03c29

Browse files
committed
validate uncompressed dump size on both client and server
1 parent f18325e commit 5b03c29

File tree

6 files changed

+48
-14
lines changed

6 files changed

+48
-14
lines changed

apps/proxy/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
"type-check": "tsc --noEmit"
1111
},
1212
"dependencies": {
13-
"@electric-sql/pglite": "0.2.0",
13+
"@electric-sql/pglite": "0.2.2",
1414
"@supabase/supabase-js": "^2.45.1",
1515
"find-up": "^7.0.0",
1616
"pg-gateway": "0.3.0-alpha.6",

apps/web/app/api/databases/[id]/upload/route.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { Readable } from 'stream'
66
import { createClient } from '~/utils/supabase/server'
77
import { createScramSha256Data } from 'pg-gateway'
88
import { generateDatabasePassword } from '~/utils/generate-database-password'
9+
import { getUncompressedSizeInMB } from '~/utils/get-uncompressed-size-in-mb'
910

1011
const wildcardDomain = process.env.NEXT_PUBLIC_WILDCARD_DOMAIN ?? 'db.example.com'
1112
const s3Client = new S3Client({ forcePathStyle: true })
@@ -62,8 +63,7 @@ export async function POST(
6263
)
6364
}
6465

65-
const dumpSizeInMB = dump.size / (1024 * 1024)
66-
if (dumpSizeInMB > 100) {
66+
if ((await getUncompressedSizeInMB(dump)) > 100) {
6767
return NextResponse.json(
6868
{
6969
success: false,

apps/web/data/deployed-databases/deployed-database-create-mutation.ts

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { useMutation, UseMutationOptions, useQueryClient } from '@tanstack/react
22
import { getDeployedDatabasesQueryKey } from './deployed-databases-query'
33
import { useApp } from '~/components/app-provider'
44
import type { DatabaseUploadResponse } from '~/app/api/databases/[id]/upload/route'
5+
import { getUncompressedSizeInMB } from '~/utils/get-uncompressed-size-in-mb'
56

67
export type DeployedDatabaseCreateVariables = {
78
databaseId: string
@@ -33,14 +34,12 @@ export const useDeployedDatabaseCreateMutation = ({
3334

3435
const db = await dbManager.getDbInstance(variables.databaseId)
3536

36-
const dump = await db.dumpDataDir()
37-
const dumpSizeInMB = dump.size / (1024 * 1024)
38-
if (dumpSizeInMB > 100) {
37+
const dump = await db.dumpDataDir('gzip')
38+
if ((await getUncompressedSizeInMB(dump)) > 100) {
3939
throw new Error("You can't deploy a database that is bigger than 100MB")
4040
}
41-
4241
const formData = new FormData()
43-
formData.append('dump', dump, 'dump.tar')
42+
formData.append('dump', dump, 'dump.tar.gz')
4443
formData.append('name', variables.name ?? 'My database')
4544
formData.append('created_at', variables.createdAt.toISOString())
4645

@@ -66,3 +65,29 @@ export const useDeployedDatabaseCreateMutation = ({
6665
...options,
6766
})
6867
}
68+
69+
export async function zip(file: Blob | File): Promise<Uint8Array> {
70+
const cs = new CompressionStream('gzip')
71+
const writer = cs.writable.getWriter()
72+
const reader = cs.readable.getReader()
73+
74+
writer.write(file)
75+
writer.close()
76+
77+
const chunks: Uint8Array[] = []
78+
79+
while (true) {
80+
const { value, done } = await reader.read()
81+
if (done) break
82+
if (value) chunks.push(value)
83+
}
84+
85+
const compressed = new Uint8Array(chunks.reduce((acc, chunk) => acc + chunk.length, 0))
86+
let offset = 0
87+
chunks.forEach((chunk) => {
88+
compressed.set(chunk, offset)
89+
offset += chunk.length
90+
})
91+
92+
return compressed
93+
}

apps/web/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
"@aws-sdk/client-s3": "^3.620.1",
1515
"@aws-sdk/lib-storage": "^3.620.1",
1616
"@dagrejs/dagre": "^1.1.2",
17-
"@electric-sql/pglite": "0.2.0",
17+
"@electric-sql/pglite": "0.2.2",
1818
"@gregnr/postgres-meta": "^0.82.0-dev.2",
1919
"@monaco-editor/react": "^4.6.0",
2020
"@postgres-new/supabase": "*",
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
/**
2+
* Get the uncompressed size of a gzipped file in MB
3+
*/
4+
export async function getUncompressedSizeInMB(gzippedData: Blob | File): Promise<number> {
5+
// The last 4 bytes of the file contain the uncompressed size
6+
const arrayBuffer = await gzippedData.slice(-4).arrayBuffer()
7+
const view = new DataView(arrayBuffer)
8+
return view.getUint32(0, true) / (1024 * 1024)
9+
}

package-lock.json

Lines changed: 5 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)