This commit is contained in:
2026-07-12 11:36:01 +02:00
parent 1f180a2e53
commit 062054f450
46 changed files with 976 additions and 1691 deletions
+38 -41
View File
@@ -1,5 +1,10 @@
<script lang="ts">
import { AES, Hex } from 'occulto'
import {
deriveKey, generateKey, encrypt, randomBytes,
bytesToHex, encode,
create as apiCreate,
type FileDTO, type ServerNote
} from '@cryptgeon/shared'
import { t } from 'svelte-intl-precompile'
import { blur } from 'svelte/transition'
@@ -14,14 +19,8 @@
import Result, { type NoteResult } from '$lib/ui/NoteResult.svelte'
import Switch from '$lib/ui/Switch.svelte'
import TextArea from '$lib/ui/TextArea.svelte'
import { Adapters, API, PayloadToLargeError, type FileDTO, type Note } from 'cryptgeon/shared'
let note: Note = $state({
contents: '',
meta: { type: 'text' },
views: 1,
expiration: 60,
})
let note: { views: number; expiration: number } = $state({ views: 1, expiration: 60 })
let files: FileDTO[] = $state([])
let result: NoteResult | null = $state(null)
let advanced = $state(false)
@@ -31,6 +30,7 @@
let description = $state('')
let loading: string | null = $state(null)
let isPasting = $state(false)
let textContent = $state('')
$effect(() => {
if (!advanced) {
@@ -50,13 +50,7 @@
})
$effect(() => {
note.meta.type = isFile ? 'file' : 'text'
})
$effect(() => {
if (!isFile) {
note.contents = ''
}
if (!isFile) textContent = ''
})
async function handlePaste(e: ClipboardEvent) {
@@ -98,11 +92,12 @@
const name =
file.name || `pasted-file-${Date.now()}-${Math.round(Math.random() * 1000)}${ext}`
const renamed = new File([file], name, { type: file.type })
const data = new Uint8Array(await renamed.arrayBuffer())
return {
name: renamed.name,
mime: renamed.type,
size: renamed.size,
type: renamed.type,
contents: new Uint8Array(await renamed.arrayBuffer()),
data,
}
})
)
@@ -122,40 +117,42 @@
try {
loading = $t('common.encrypting')
const derived = customPassword && (await AES.derive(customPassword))
const key = derived ? derived[0] : await AES.generateKey()
const salt = customPassword ? randomBytes(16) : null
const key = customPassword
? deriveKey(customPassword, salt!)
: generateKey()
const data: Note = {
contents: '',
meta: note.meta,
}
if (derived) data.meta.derivation = derived[1]
let inner: Uint8Array
if (isFile) {
if (files.length === 0) throw new EmptyContentError()
data.contents = await Adapters.Files.encrypt(files, key)
inner = encode({ type: 'files', data: files })
} else {
if (note.contents === '') throw new EmptyContentError()
data.contents = await Adapters.Text.encrypt(note.contents, key)
if (textContent === '') throw new EmptyContentError()
inner = encode({ type: 'text', data: textContent })
}
const data = encrypt(inner, key)
const extra = customPassword
? encode({ salt: salt!, N: 32768, r: 8, p: 1 })
: new Uint8Array()
const serverNote: ServerNote = {
meta: {
...(timeExpiration ? { expiration: parseInt(note.expiration as any) } : { views: parseInt(note.views as any) }),
extra,
},
data,
}
if (timeExpiration) data.expiration = parseInt(note.expiration as any)
else data.views = parseInt(note.views as any)
loading = $t('common.uploading')
const response = await API.create(data)
const response = await apiCreate(serverNote)
result = {
id: response.id,
password: customPassword ? undefined : Hex.encode(key),
password: customPassword ? undefined : bytesToHex(key),
}
notify.success($t('home.messages.note_created'))
} catch (e) {
if (e instanceof PayloadToLargeError) {
notify.error($t('home.errors.note_too_big'))
} else if (e instanceof EmptyContentError) {
notify.error($t('home.errors.empty_content'))
} else {
console.error(e)
notify.error($t('home.errors.note_error'))
}
console.error(e)
notify.error($t('home.errors.note_error'))
} finally {
loading = null
}
@@ -183,7 +180,7 @@
<TextArea
data-testid="text-field"
label={$t('common.note')}
bind:value={note.contents}
bind:value={textContent}
placeholder="..."
/>
{/if}
@@ -284,4 +281,4 @@
transform: rotate(360deg);
}
}
</style>
</style>