Compare commits
No commits in common. "35b1378aa79b69b0375b92d91cfa1488a587eb56" and "59c3fc69a255c54475daa6a2efad3ae8a4b3efd8" have entirely different histories.
35b1378aa7
...
59c3fc69a2
@ -11,6 +11,7 @@ import {
|
|||||||
hexToNpub,
|
hexToNpub,
|
||||||
parseNostrEvent,
|
parseNostrEvent,
|
||||||
parseCreateSignatureEventContent,
|
parseCreateSignatureEventContent,
|
||||||
|
SigitMetaParseError,
|
||||||
SigitStatus,
|
SigitStatus,
|
||||||
SignStatus
|
SignStatus
|
||||||
} from '../utils'
|
} from '../utils'
|
||||||
@ -20,7 +21,6 @@ import { Event } from 'nostr-tools'
|
|||||||
import store from '../store/store'
|
import store from '../store/store'
|
||||||
import { AuthState } from '../store/auth/types'
|
import { AuthState } from '../store/auth/types'
|
||||||
import { NostrController } from '../controllers'
|
import { NostrController } from '../controllers'
|
||||||
import { MetaParseError } from '../types/errors/MetaParseError'
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Flattened interface that combines properties `Meta`, `CreateSignatureEventContent`,
|
* Flattened interface that combines properties `Meta`, `CreateSignatureEventContent`,
|
||||||
@ -247,7 +247,7 @@ export const useSigitMeta = (meta: Meta): FlatMeta => {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error instanceof MetaParseError) {
|
if (error instanceof SigitMetaParseError) {
|
||||||
toast.error(error.message)
|
toast.error(error.message)
|
||||||
}
|
}
|
||||||
console.error(error)
|
console.error(error)
|
||||||
|
@ -17,10 +17,8 @@ export const StickySideColumns = ({
|
|||||||
<div className={`${styles.sidesWrap} ${styles.files}`}>
|
<div className={`${styles.sidesWrap} ${styles.files}`}>
|
||||||
<div className={styles.sides}>{left}</div>
|
<div className={styles.sides}>{left}</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div id="content-preview" className={styles.content}>
|
||||||
<div id="content-preview" className={styles.content}>
|
{children}
|
||||||
{children}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.sidesWrap}>
|
<div className={styles.sidesWrap}>
|
||||||
<div className={styles.sides}>{right}</div>
|
<div className={styles.sides}>{right}</div>
|
||||||
|
@ -514,9 +514,6 @@ export const CreatePage = () => {
|
|||||||
return (
|
return (
|
||||||
file.pages?.flatMap((page, index) => {
|
file.pages?.flatMap((page, index) => {
|
||||||
return page.drawnFields.map((drawnField) => {
|
return page.drawnFields.map((drawnField) => {
|
||||||
if (!drawnField.counterpart) {
|
|
||||||
throw new Error('Missing counterpart')
|
|
||||||
}
|
|
||||||
return {
|
return {
|
||||||
type: drawnField.type,
|
type: drawnField.type,
|
||||||
location: {
|
location: {
|
||||||
@ -673,7 +670,6 @@ export const CreatePage = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const generateCreateSignature = async (
|
const generateCreateSignature = async (
|
||||||
markConfig: Mark[],
|
|
||||||
fileHashes: {
|
fileHashes: {
|
||||||
[key: string]: string
|
[key: string]: string
|
||||||
},
|
},
|
||||||
@ -681,6 +677,7 @@ export const CreatePage = () => {
|
|||||||
) => {
|
) => {
|
||||||
const signers = users.filter((user) => user.role === UserRole.signer)
|
const signers = users.filter((user) => user.role === UserRole.signer)
|
||||||
const viewers = users.filter((user) => user.role === UserRole.viewer)
|
const viewers = users.filter((user) => user.role === UserRole.viewer)
|
||||||
|
const markConfig = createMarks(fileHashes)
|
||||||
|
|
||||||
const content: CreateSignatureEventContent = {
|
const content: CreateSignatureEventContent = {
|
||||||
signers: signers.map((signer) => hexToNpub(signer.pubkey)),
|
signers: signers.map((signer) => hexToNpub(signer.pubkey)),
|
||||||
@ -724,152 +721,131 @@ export const CreatePage = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const handleCreate = async () => {
|
const handleCreate = async () => {
|
||||||
try {
|
if (!validateInputs()) return
|
||||||
if (!validateInputs()) return
|
|
||||||
|
|
||||||
setIsLoading(true)
|
setIsLoading(true)
|
||||||
setLoadingSpinnerDesc('Generating file hashes')
|
setLoadingSpinnerDesc('Generating file hashes')
|
||||||
const fileHashes = await generateFileHashes()
|
const fileHashes = await generateFileHashes()
|
||||||
if (!fileHashes) {
|
if (!fileHashes) {
|
||||||
|
setIsLoading(false)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
setLoadingSpinnerDesc('Generating encryption key')
|
||||||
|
const encryptionKey = await generateEncryptionKey()
|
||||||
|
|
||||||
|
if (await isOnline()) {
|
||||||
|
setLoadingSpinnerDesc('generating files.zip')
|
||||||
|
const arrayBuffer = await generateFilesZip()
|
||||||
|
if (!arrayBuffer) {
|
||||||
setIsLoading(false)
|
setIsLoading(false)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
setLoadingSpinnerDesc('Generating encryption key')
|
setLoadingSpinnerDesc('Encrypting files.zip')
|
||||||
const encryptionKey = await generateEncryptionKey()
|
const encryptedArrayBuffer = await encryptZipFile(
|
||||||
|
arrayBuffer,
|
||||||
|
encryptionKey
|
||||||
|
)
|
||||||
|
|
||||||
if (await isOnline()) {
|
setLoadingSpinnerDesc('Uploading files.zip to file storage')
|
||||||
setLoadingSpinnerDesc('generating files.zip')
|
const fileUrl = await uploadFile(encryptedArrayBuffer)
|
||||||
const arrayBuffer = await generateFilesZip()
|
if (!fileUrl) {
|
||||||
if (!arrayBuffer) {
|
setIsLoading(false)
|
||||||
setIsLoading(false)
|
return
|
||||||
return
|
}
|
||||||
}
|
|
||||||
|
|
||||||
setLoadingSpinnerDesc('Encrypting files.zip')
|
setLoadingSpinnerDesc('Generating create signature')
|
||||||
const encryptedArrayBuffer = await encryptZipFile(
|
const createSignature = await generateCreateSignature(fileHashes, fileUrl)
|
||||||
arrayBuffer,
|
if (!createSignature) {
|
||||||
encryptionKey
|
setIsLoading(false)
|
||||||
)
|
return
|
||||||
|
}
|
||||||
|
|
||||||
const markConfig = createMarks(fileHashes)
|
setLoadingSpinnerDesc('Generating keys for decryption')
|
||||||
|
|
||||||
setLoadingSpinnerDesc('Uploading files.zip to file storage')
|
// generate key pairs for decryption
|
||||||
const fileUrl = await uploadFile(encryptedArrayBuffer)
|
const pubkeys = users.map((user) => user.pubkey)
|
||||||
if (!fileUrl) {
|
// also add creator in the list
|
||||||
setIsLoading(false)
|
if (pubkeys.includes(usersPubkey!)) {
|
||||||
return
|
pubkeys.push(usersPubkey!)
|
||||||
}
|
}
|
||||||
|
|
||||||
setLoadingSpinnerDesc('Generating create signature')
|
const keys = await generateKeys(pubkeys, encryptionKey)
|
||||||
const createSignature = await generateCreateSignature(
|
|
||||||
markConfig,
|
|
||||||
fileHashes,
|
|
||||||
fileUrl
|
|
||||||
)
|
|
||||||
if (!createSignature) {
|
|
||||||
setIsLoading(false)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
setLoadingSpinnerDesc('Generating keys for decryption')
|
if (!keys) {
|
||||||
|
setIsLoading(false)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const meta: Meta = {
|
||||||
|
createSignature,
|
||||||
|
keys,
|
||||||
|
modifiedAt: unixNow(),
|
||||||
|
docSignatures: {}
|
||||||
|
}
|
||||||
|
|
||||||
// generate key pairs for decryption
|
setLoadingSpinnerDesc('Updating user app data')
|
||||||
const pubkeys = users.map((user) => user.pubkey)
|
const event = await updateUsersAppData(meta)
|
||||||
// also add creator in the list
|
if (!event) {
|
||||||
if (pubkeys.includes(usersPubkey!)) {
|
setIsLoading(false)
|
||||||
pubkeys.push(usersPubkey!)
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const keys = await generateKeys(pubkeys, encryptionKey)
|
setLoadingSpinnerDesc('Sending notifications to counterparties')
|
||||||
|
const promises = sendNotifications(meta)
|
||||||
|
|
||||||
if (!keys) {
|
await Promise.all(promises)
|
||||||
setIsLoading(false)
|
.then(() => {
|
||||||
return
|
toast.success('Notifications sent successfully')
|
||||||
}
|
})
|
||||||
const meta: Meta = {
|
.catch(() => {
|
||||||
createSignature,
|
toast.error('Failed to publish notifications')
|
||||||
keys,
|
|
||||||
modifiedAt: unixNow(),
|
|
||||||
docSignatures: {}
|
|
||||||
}
|
|
||||||
|
|
||||||
setLoadingSpinnerDesc('Updating user app data')
|
|
||||||
const event = await updateUsersAppData(meta)
|
|
||||||
if (!event) {
|
|
||||||
setIsLoading(false)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
setLoadingSpinnerDesc('Sending notifications to counterparties')
|
|
||||||
const promises = sendNotifications(meta)
|
|
||||||
|
|
||||||
await Promise.all(promises)
|
|
||||||
.then(() => {
|
|
||||||
toast.success('Notifications sent successfully')
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
toast.error('Failed to publish notifications')
|
|
||||||
})
|
|
||||||
|
|
||||||
navigate(appPrivateRoutes.sign, { state: { meta: meta } })
|
|
||||||
} else {
|
|
||||||
const zip = new JSZip()
|
|
||||||
|
|
||||||
selectedFiles.forEach((file) => {
|
|
||||||
zip.file(`files/${file.name}`, file)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const markConfig = createMarks(fileHashes)
|
navigate(appPrivateRoutes.sign, { state: { meta: meta } })
|
||||||
|
} else {
|
||||||
|
const zip = new JSZip()
|
||||||
|
|
||||||
setLoadingSpinnerDesc('Generating create signature')
|
selectedFiles.forEach((file) => {
|
||||||
const createSignature = await generateCreateSignature(
|
zip.file(`files/${file.name}`, file)
|
||||||
markConfig,
|
})
|
||||||
fileHashes,
|
|
||||||
''
|
|
||||||
)
|
|
||||||
if (!createSignature) {
|
|
||||||
setIsLoading(false)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const meta: Meta = {
|
setLoadingSpinnerDesc('Generating create signature')
|
||||||
createSignature,
|
const createSignature = await generateCreateSignature(fileHashes, '')
|
||||||
modifiedAt: unixNow(),
|
if (!createSignature) {
|
||||||
docSignatures: {}
|
setIsLoading(false)
|
||||||
}
|
return
|
||||||
|
|
||||||
// add meta to zip
|
|
||||||
try {
|
|
||||||
const stringifiedMeta = JSON.stringify(meta, null, 2)
|
|
||||||
zip.file('meta.json', stringifiedMeta)
|
|
||||||
} catch (err) {
|
|
||||||
console.error(err)
|
|
||||||
toast.error('An error occurred in converting meta json to string')
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
const arrayBuffer = await generateZipFile(zip)
|
|
||||||
if (!arrayBuffer) {
|
|
||||||
setIsLoading(false)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
setLoadingSpinnerDesc('Encrypting zip file')
|
|
||||||
const encryptedArrayBuffer = await encryptZipFile(
|
|
||||||
arrayBuffer,
|
|
||||||
encryptionKey
|
|
||||||
)
|
|
||||||
|
|
||||||
await handleOfflineFlow(encryptedArrayBuffer, encryptionKey)
|
|
||||||
}
|
}
|
||||||
} catch (error) {
|
|
||||||
if (error instanceof Error) {
|
const meta: Meta = {
|
||||||
toast.error(error.message)
|
createSignature,
|
||||||
|
modifiedAt: unixNow(),
|
||||||
|
docSignatures: {}
|
||||||
}
|
}
|
||||||
console.error(error)
|
|
||||||
} finally {
|
// add meta to zip
|
||||||
setIsLoading(false)
|
try {
|
||||||
|
const stringifiedMeta = JSON.stringify(meta, null, 2)
|
||||||
|
zip.file('meta.json', stringifiedMeta)
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err)
|
||||||
|
toast.error('An error occurred in converting meta json to string')
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
const arrayBuffer = await generateZipFile(zip)
|
||||||
|
if (!arrayBuffer) {
|
||||||
|
setIsLoading(false)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
setLoadingSpinnerDesc('Encrypting zip file')
|
||||||
|
const encryptedArrayBuffer = await encryptZipFile(
|
||||||
|
arrayBuffer,
|
||||||
|
encryptionKey
|
||||||
|
)
|
||||||
|
|
||||||
|
await handleOfflineFlow(encryptedArrayBuffer, encryptionKey)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -542,13 +542,10 @@ export const SignPage = () => {
|
|||||||
setLoadingSpinnerDesc('Signing nostr event')
|
setLoadingSpinnerDesc('Signing nostr event')
|
||||||
|
|
||||||
const prevSig = getPrevSignersSig(hexToNpub(usersPubkey!))
|
const prevSig = getPrevSignersSig(hexToNpub(usersPubkey!))
|
||||||
if (!prevSig) {
|
if (!prevSig) return
|
||||||
setIsLoading(false)
|
|
||||||
toast.error('Previous signature is invalid')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const marks = getSignerMarksForMeta() || []
|
const marks = getSignerMarksForMeta()
|
||||||
|
if (!marks) return
|
||||||
|
|
||||||
const signedEvent = await signEventForMeta({ prevSig, marks })
|
const signedEvent = await signEventForMeta({ prevSig, marks })
|
||||||
if (!signedEvent) return
|
if (!signedEvent) return
|
||||||
|
@ -1,23 +0,0 @@
|
|||||||
import { Jsonable } from '.'
|
|
||||||
|
|
||||||
// Reuse common error messages for meta parsing
|
|
||||||
export enum MetaParseErrorType {
|
|
||||||
'PARSE_ERROR_EVENT' = 'error occurred in parsing the create signature event',
|
|
||||||
'PARSE_ERROR_SIGNATURE_EVENT_CONTENT' = "err in parsing the createSignature event's content"
|
|
||||||
}
|
|
||||||
|
|
||||||
export class MetaParseError extends Error {
|
|
||||||
public readonly context?: Jsonable
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
message: string,
|
|
||||||
options: { cause?: Error; context?: Jsonable } = {}
|
|
||||||
) {
|
|
||||||
const { cause, context } = options
|
|
||||||
|
|
||||||
super(message, { cause })
|
|
||||||
this.name = this.constructor.name
|
|
||||||
|
|
||||||
this.context = context
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,29 +0,0 @@
|
|||||||
export type Jsonable =
|
|
||||||
| string
|
|
||||||
| number
|
|
||||||
| boolean
|
|
||||||
| null
|
|
||||||
| undefined
|
|
||||||
| readonly Jsonable[]
|
|
||||||
| { readonly [key: string]: Jsonable }
|
|
||||||
| { toJSON(): Jsonable }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handle errors
|
|
||||||
* Wraps the errors without message property and stringify to a message so we can use it later
|
|
||||||
* @param error
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
export function handleError(error: unknown): Error {
|
|
||||||
if (error instanceof Error) return error
|
|
||||||
|
|
||||||
// No message error, wrap it and stringify
|
|
||||||
let stringified = 'Unable to stringify the thrown value'
|
|
||||||
try {
|
|
||||||
stringified = JSON.stringify(error)
|
|
||||||
} catch (error) {
|
|
||||||
console.error(stringified, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Error(`Wrapped Error: ${stringified}`)
|
|
||||||
}
|
|
@ -47,7 +47,7 @@ const filterMarksByPubkey = (marks: Mark[], pubkey: string): Mark[] => {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Takes Signed Doc Signatures part of Meta and extracts
|
* Takes Signed Doc Signatures part of Meta and extracts
|
||||||
* all Marks into one flat array, regardless of the user.
|
* all Marks into one flar array, regardless of the user.
|
||||||
* @param meta
|
* @param meta
|
||||||
*/
|
*/
|
||||||
const extractMarksFromSignedMeta = (meta: Meta): Mark[] => {
|
const extractMarksFromSignedMeta = (meta: Meta): Mark[] => {
|
||||||
|
@ -3,11 +3,6 @@ import { fromUnixTimestamp, parseJson } from '.'
|
|||||||
import { Event, verifyEvent } from 'nostr-tools'
|
import { Event, verifyEvent } from 'nostr-tools'
|
||||||
import { toast } from 'react-toastify'
|
import { toast } from 'react-toastify'
|
||||||
import { extractFileExtensions } from './file'
|
import { extractFileExtensions } from './file'
|
||||||
import { handleError } from '../types/errors'
|
|
||||||
import {
|
|
||||||
MetaParseError,
|
|
||||||
MetaParseErrorType
|
|
||||||
} from '../types/errors/MetaParseError'
|
|
||||||
|
|
||||||
export enum SignStatus {
|
export enum SignStatus {
|
||||||
Signed = 'Signed',
|
Signed = 'Signed',
|
||||||
@ -22,6 +17,58 @@ export enum SigitStatus {
|
|||||||
Complete = 'Completed'
|
Complete = 'Completed'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Jsonable =
|
||||||
|
| string
|
||||||
|
| number
|
||||||
|
| boolean
|
||||||
|
| null
|
||||||
|
| undefined
|
||||||
|
| readonly Jsonable[]
|
||||||
|
| { readonly [key: string]: Jsonable }
|
||||||
|
| { toJSON(): Jsonable }
|
||||||
|
|
||||||
|
export class SigitMetaParseError extends Error {
|
||||||
|
public readonly context?: Jsonable
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
message: string,
|
||||||
|
options: { cause?: Error; context?: Jsonable } = {}
|
||||||
|
) {
|
||||||
|
const { cause, context } = options
|
||||||
|
|
||||||
|
super(message, { cause })
|
||||||
|
this.name = this.constructor.name
|
||||||
|
|
||||||
|
this.context = context
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle meta errors
|
||||||
|
* Wraps the errors without message property and stringify to a message so we can use it later
|
||||||
|
* @param error
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
function handleError(error: unknown): Error {
|
||||||
|
if (error instanceof Error) return error
|
||||||
|
|
||||||
|
// No message error, wrap it and stringify
|
||||||
|
let stringified = 'Unable to stringify the thrown value'
|
||||||
|
try {
|
||||||
|
stringified = JSON.stringify(error)
|
||||||
|
} catch (error) {
|
||||||
|
console.error(stringified, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Error(`[SiGit Error]: ${stringified}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reuse common error messages for meta parsing
|
||||||
|
export enum SigitMetaParseErrorType {
|
||||||
|
'PARSE_ERROR_EVENT' = 'error occurred in parsing the create signature event',
|
||||||
|
'PARSE_ERROR_SIGNATURE_EVENT_CONTENT' = "err in parsing the createSignature event's content"
|
||||||
|
}
|
||||||
|
|
||||||
export interface SigitCardDisplayInfo {
|
export interface SigitCardDisplayInfo {
|
||||||
createdAt?: number
|
createdAt?: number
|
||||||
title?: string
|
title?: string
|
||||||
@ -42,7 +89,7 @@ export const parseNostrEvent = async (raw: string): Promise<Event> => {
|
|||||||
const event = await parseJson<Event>(raw)
|
const event = await parseJson<Event>(raw)
|
||||||
return event
|
return event
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new MetaParseError(MetaParseErrorType.PARSE_ERROR_EVENT, {
|
throw new SigitMetaParseError(SigitMetaParseErrorType.PARSE_ERROR_EVENT, {
|
||||||
cause: handleError(error),
|
cause: handleError(error),
|
||||||
context: raw
|
context: raw
|
||||||
})
|
})
|
||||||
@ -62,8 +109,8 @@ export const parseCreateSignatureEventContent = async (
|
|||||||
await parseJson<CreateSignatureEventContent>(raw)
|
await parseJson<CreateSignatureEventContent>(raw)
|
||||||
return createSignatureEventContent
|
return createSignatureEventContent
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new MetaParseError(
|
throw new SigitMetaParseError(
|
||||||
MetaParseErrorType.PARSE_ERROR_SIGNATURE_EVENT_CONTENT,
|
SigitMetaParseErrorType.PARSE_ERROR_SIGNATURE_EVENT_CONTENT,
|
||||||
{
|
{
|
||||||
cause: handleError(error),
|
cause: handleError(error),
|
||||||
context: raw
|
context: raw
|
||||||
@ -118,7 +165,7 @@ export const extractSigitCardDisplayInfo = async (meta: Meta) => {
|
|||||||
|
|
||||||
return sigitInfo
|
return sigitInfo
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error instanceof MetaParseError) {
|
if (error instanceof SigitMetaParseError) {
|
||||||
toast.error(error.message)
|
toast.error(error.message)
|
||||||
console.error(error.name, error.message, error.cause, error.context)
|
console.error(error.name, error.message, error.cause, error.context)
|
||||||
} else {
|
} else {
|
||||||
|
Loading…
Reference in New Issue
Block a user