167 lines
4.8 KiB
TypeScript
167 lines
4.8 KiB
TypeScript
import axios, { isAxiosError } from 'axios'
|
|
import { NostrEvent, NDKKind } from '@nostr-dev-kit/ndk'
|
|
import { type MediaOperations } from '.'
|
|
import { store } from 'store'
|
|
import { log, LogType, now } from 'utils'
|
|
import { BaseError, handleError } from 'types'
|
|
|
|
// https://github.com/quentintaranpino/nostrcheck-server/blob/main/DOCS.md#media-post
|
|
// Response object (other fields omitted for brevity)
|
|
// {
|
|
// "status": "success",
|
|
// "nip94_event": {
|
|
// "tags": [
|
|
// [
|
|
// "url",
|
|
// "https://nostrcheck.me/media/62c76eb094369d938f5895442eef7f53ebbf019f69707d64e77d4d182b609309/c35277dbcedebb0e3b80361762c8baadb66dcdfb6396949e50630159a472c3b2.webp"
|
|
// ],
|
|
// ],
|
|
// }
|
|
// }
|
|
|
|
interface Response {
|
|
status: 'success' | string
|
|
nip94_event?: {
|
|
tags?: string[][]
|
|
}
|
|
}
|
|
|
|
enum HandledErrorType {
|
|
'PUBKEY' = 'Failed to get public key.',
|
|
'SIGN' = 'Failed to sign the event.',
|
|
'AXIOS_REQ' = 'Image upload failed. Try another host from the dropdown.',
|
|
'AXIOS_RES' = 'Image upload failed. Reason: ',
|
|
'AXIOS_ERR' = 'Image upload failed.',
|
|
'NOSTR_CHECK_NO_SUCCESS' = 'Image upload was unsuccesfull.',
|
|
'NOSTR_CHECK_BAD_EVENT' = 'Image upload failed. Please try again.'
|
|
}
|
|
|
|
export class NostrCheckServer implements MediaOperations {
|
|
#media = 'api/v2/media'
|
|
#url: string
|
|
|
|
constructor(url: string) {
|
|
this.#url = url[url.length - 1] === '/' ? url : `${url}/`
|
|
}
|
|
|
|
post = async (file: File) => {
|
|
const url = `${this.#url}${this.#media}`
|
|
const auth = await this.auth()
|
|
try {
|
|
const response = await axios.postForm<Response>(
|
|
url,
|
|
{
|
|
uploadType: 'media',
|
|
file: file
|
|
},
|
|
{
|
|
headers: {
|
|
Authorization: 'Nostr ' + auth,
|
|
'Content-Type': 'multipart/form-data'
|
|
},
|
|
responseType: 'json'
|
|
}
|
|
)
|
|
|
|
if (response.data.status !== 'success') {
|
|
throw new BaseError(HandledErrorType.NOSTR_CHECK_NO_SUCCESS, {
|
|
context: { ...response.data }
|
|
})
|
|
}
|
|
|
|
if (
|
|
response.data &&
|
|
response.data.nip94_event &&
|
|
response.data.nip94_event.tags &&
|
|
response.data.nip94_event.tags.length
|
|
) {
|
|
// Return first 'url' tag we find on the returned nip94 event
|
|
const imageUrl = response.data.nip94_event.tags.find(
|
|
(item) => item[0] === 'url'
|
|
)
|
|
|
|
if (imageUrl) return imageUrl[1]
|
|
}
|
|
|
|
throw new BaseError(HandledErrorType.NOSTR_CHECK_BAD_EVENT, {
|
|
context: { ...response.data }
|
|
})
|
|
} catch (error) {
|
|
// Handle axios errors
|
|
if (isAxiosError(error)) {
|
|
if (error.request) {
|
|
// The request was made but no response was received
|
|
throw new BaseError(HandledErrorType.AXIOS_REQ, {
|
|
cause: error
|
|
})
|
|
} else if (error.response) {
|
|
// The request was made and the server responded with a status code
|
|
// that falls out of the range of 2xx
|
|
// nostrcheck-server can return different results, including message or description
|
|
const data = error.response.data
|
|
let message = error.message
|
|
if (data) {
|
|
message = data?.message || data?.description || error.message
|
|
}
|
|
throw new BaseError(HandledErrorType.AXIOS_RES + message, {
|
|
cause: error
|
|
})
|
|
} else {
|
|
// Something happened in setting up the request that triggered an Error
|
|
throw new BaseError(HandledErrorType.AXIOS_ERR, {
|
|
cause: error
|
|
})
|
|
}
|
|
} else if (error instanceof BaseError) {
|
|
throw error
|
|
} else {
|
|
throw handleError(error)
|
|
}
|
|
}
|
|
}
|
|
|
|
auth = async () => {
|
|
try {
|
|
const url = `${this.#url}${this.#media}`
|
|
|
|
let hexPubkey: string | undefined
|
|
const userState = store.getState().user
|
|
if (userState.auth && userState.user?.pubkey) {
|
|
hexPubkey = userState.user.pubkey as string
|
|
} else {
|
|
try {
|
|
hexPubkey = (await window.nostr?.getPublicKey()) as string
|
|
} catch (error) {
|
|
log(true, LogType.Error, `Could not get pubkey`, error)
|
|
}
|
|
}
|
|
|
|
if (!hexPubkey) {
|
|
throw new BaseError(HandledErrorType.PUBKEY)
|
|
}
|
|
|
|
const unsignedEvent: NostrEvent = {
|
|
content: '',
|
|
created_at: now(),
|
|
kind: NDKKind.HttpAuth,
|
|
pubkey: hexPubkey,
|
|
tags: [
|
|
['u', url],
|
|
['method', 'POST']
|
|
]
|
|
}
|
|
|
|
const signedEvent = await window.nostr?.signEvent(unsignedEvent)
|
|
return btoa(JSON.stringify(signedEvent))
|
|
} catch (error) {
|
|
if (error instanceof BaseError) {
|
|
throw error
|
|
}
|
|
|
|
throw new BaseError(HandledErrorType.SIGN, {
|
|
cause: handleError(error)
|
|
})
|
|
}
|
|
}
|
|
}
|