refactor: extract payload pack/unpack pipeline into @cryptgeon/shared

This commit is contained in:
2026-09-12 10:01:49 +02:00
parent 77b7ae1de6
commit 82900adef8
7 changed files with 133 additions and 60 deletions
+2 -3
View File
@@ -3,7 +3,7 @@ import { access, constants, writeFile } from 'node:fs/promises'
import { basename, resolve } from 'node:path'
import { decode } from '@msgpack/msgpack'
import pretty from 'pretty-bytes'
import { decrypt, deriveKey, setServer, info, get, decompress } from '@cryptgeon/shared'
import { deriveKey, setServer, info, get, unpackContent } from '@cryptgeon/shared'
export async function download(url: URL, all: boolean, suggestedPassword?: string) {
setServer(url.origin)
@@ -32,8 +32,7 @@ export async function download(url: URL, all: boolean, suggestedPassword?: strin
const note = await get(id)
if (!note) throw new Error('Could not load note')
const decrypted = decrypt(note.data, key)
const content = decode(decompress(decrypted)) as any
const content = unpackContent(note.data, key)
switch (content.type) {
case 'files':
+24 -29
View File
@@ -1,43 +1,38 @@
import { readFile } from 'node:fs/promises'
import { basename } from 'node:path'
import { encode } from '@msgpack/msgpack'
import mime from 'mime'
import { encrypt, generateKey, deriveKey, randomBytes, getServer, create, compress } from '@cryptgeon/shared'
import { getServer, create, packContent, type FileDTO } from '@cryptgeon/shared'
export type UploadOptions = { views?: number; expiration?: number; password?: string }
export async function upload(input: string | string[], options: UploadOptions): Promise<string> {
const { password, ...noteOptions } = options
let key: Uint8Array
let extra = new Uint8Array()
if (password) {
const salt = randomBytes(16)
key = deriveKey(password, salt)
extra = encode({ salt, N: 32768, r: 8, p: 1 })
} else {
key = generateKey()
}
const payload = packContent(
typeof input === 'string'
? { type: 'text', text: input }
: { type: 'files', files: await fileDTOSfromPaths(input) },
password
)
let inner: Uint8Array
if (typeof input === 'string') {
inner = encode({ type: 'text', data: input })
} else {
const files = await Promise.all(
input.map(async (path) => {
const data = new Uint8Array(await readFile(path))
const extension = path.substring(path.indexOf('.') + 1)
const type = mime.getType(extension) ?? 'application/octet-stream'
return { name: basename(path), mime: type, size: data.length, data }
})
)
inner = encode({ type: 'files', data: files })
}
const data = encrypt(compress(inner), key)
const result = await create({ meta: { ...noteOptions, extra }, data })
const result = await create({ meta: { ...noteOptions, extra: payload.extra }, data: payload.data })
let url = `${getServer()}/note/${result.id}`
if (!password) url += `#${Buffer.from(key).toString('hex')}`
if (!password) url += `#${Buffer.from(payload.key).toString('hex')}`
return url
}
async function fileDTOSfromPaths(paths: string[]): Promise<FileDTO[]> {
return Promise.all(
paths.map(async (path) => {
const extension = path.substring(path.indexOf('.') + 1)
const data = new Uint8Array(await readFile(path))
return {
name: basename(path),
mime: mime.getType(extension) ?? 'application/octet-stream',
size: data.length,
data,
}
})
)
}