diff --git a/src/js/components/buttons/Upload.tsx b/src/js/components/buttons/Upload.tsx index ef3d511db..331ffb439 100644 --- a/src/js/components/buttons/Upload.tsx +++ b/src/js/components/buttons/Upload.tsx @@ -1,5 +1,6 @@ import { useState } from 'react'; +import Key from '@/nostr/Key'; import { uploadFile } from '@/utils/uploadFile'; const Upload = (props) => { @@ -18,6 +19,7 @@ const Upload = (props) => { (errorMsg) => { setError(errorMsg); }, + Key, ); } }; diff --git a/src/js/components/create/CreateNoteForm.tsx b/src/js/components/create/CreateNoteForm.tsx index 572ed7e84..568449c77 100644 --- a/src/js/components/create/CreateNoteForm.tsx +++ b/src/js/components/create/CreateNoteForm.tsx @@ -75,20 +75,47 @@ function CreateNoteForm({ const handleFileAttachments = useCallback((files) => { if (!files) return; + const uploadApiUrl = 'https://nostr.build/api/v2/upload/files'; + for (let i = 0; i < files.length; i++) { const file = files[i]; - const formData = new FormData(); - formData.append('fileToUpload', file); - - fetch('https://nostr.build/api/upload/iris.php', { - method: 'POST', - body: formData, - }) + const authEvent = { + kind: 27235, + created_at: Math.floor(Date.now() / 1000), + content: '', + tags: [ + ['u', uploadApiUrl], + ['method', 'POST'], + ], + pubkey: Key.getPubKey(), + sig: '', + id: '', + }; + + Key.sign(authEvent) + .then((signature) => { + authEvent.sig = signature; + const authHeader = `Nostr ${btoa(JSON.stringify(authEvent))}`; + + const formData = new FormData(); + formData.append('file', file); + + return fetch(uploadApiUrl, { + method: 'POST', + body: formData, + headers: { + Authorization: authHeader, + }, + }); + }) .then(async (response) => { - const url = await response.json(); - if (url) { + const uploadResult = await response.json(); + if (response.status === 200 && uploadResult?.status === 'success') { + const url = uploadResult.data[0].url; setText((prevText) => (prevText ? `${prevText}\n\n${url}` : url)); + } else { + console.error('Upload failed', uploadResult); } }) .catch((error) => { diff --git a/src/js/components/create/TextArea.tsx b/src/js/components/create/TextArea.tsx index 99bdc3cce..73114436e 100644 --- a/src/js/components/create/TextArea.tsx +++ b/src/js/components/create/TextArea.tsx @@ -76,6 +76,7 @@ const TextArea: React.FC = ({ blob, (url) => setValue(value ? `${value}\n\n${url}` : url), (errorMsg) => console.error(errorMsg), + Key, ); } } diff --git a/src/js/nostr/Key.ts b/src/js/nostr/Key.ts index 5e940e050..f3fe6f6ad 100644 --- a/src/js/nostr/Key.ts +++ b/src/js/nostr/Key.ts @@ -2,9 +2,10 @@ import { bech32 } from 'bech32'; import { Event, generatePrivateKey, + getEventHash, getPublicKey, + getSignature, nip04, - signEvent, UnsignedEvent, } from 'nostr-tools'; @@ -124,8 +125,15 @@ export default { }, sign: async function (event: Event | UnsignedEvent): Promise { const priv = this.getPrivKey(); + if ('id' in event) { + event.id = getEventHash(event); + } else { + const hash = getEventHash(event); + (event as Event).id = hash; + } if (priv) { - return signEvent(event, priv); + (event as Event).sig = getSignature(event, priv); + return JSON.stringify(event); } else if (window.nostr) { return new Promise((resolve) => { this.processWindowNostr({ op: 'sign', data: event, callback: resolve }); diff --git a/src/js/utils/uploadFile.ts b/src/js/utils/uploadFile.ts index ce46d1968..b2934c651 100644 --- a/src/js/utils/uploadFile.ts +++ b/src/js/utils/uploadFile.ts @@ -1,15 +1,41 @@ -export const uploadFile = (file, onUrlCallback, onErrorCallback) => { - const formData = new FormData(); - formData.append('fileToUpload', file); +export const uploadFile = (file, onUrlCallback, onErrorCallback, key) => { + const uploadApiUrl = 'https://nostr.build/api/v2/upload/files'; - fetch('https://nostr.build/api/upload/iris.php', { - method: 'POST', - body: formData, - }) + const authEvent = { + kind: 27235, + created_at: Math.floor(Date.now() / 1000), + content: '', + tags: [ + ['u', uploadApiUrl], + ['method', 'POST'], + ], + pubkey: key.getPubKey(), // assuming 'key' is an instance of your Key class or has a similar interface + }; + + key + .sign(authEvent) // assuming 'key' is an instance of your Key class or has a similar interface + .then((signature) => { + (authEvent as any).sig = signature; + const authHeader = `Nostr ${btoa(JSON.stringify(authEvent))}`; + + const formData = new FormData(); + formData.append('fileToUpload', file); + + return fetch(uploadApiUrl, { + method: 'POST', + body: formData, + headers: { + Authorization: authHeader, + }, + }); + }) .then(async (response) => { - const url = await response.json(); - if (url && onUrlCallback) { + const uploadResult = await response.json(); + if (response.status === 200 && uploadResult?.status === 'success' && onUrlCallback) { + const url = uploadResult.data[0].url; onUrlCallback(url); + } else { + throw new Error('Upload failed: ' + JSON.stringify(uploadResult)); } }) .catch((error) => {