**Motivations:** - Keep dependencies up to date for security and features - Automate dependency updates in deployment script - Fix compatibility issues with major version updates (React 19, Next.js 16, nostr-tools 2.x) **Root causes:** - Dependencies were outdated - Deployment script did not update dependencies before deploying - Major version updates introduced breaking API changes **Correctifs:** - Updated all dependencies to latest versions using npm-check-updates - Modified deploy.sh to run npm-check-updates before installing dependencies - Fixed nostr-tools 2.x API changes (generatePrivateKey -> generateSecretKey, signEvent -> finalizeEvent, verifySignature -> verifyEvent) - Fixed React 19 ref types to accept null - Fixed JSX namespace issues (JSX.Element -> React.ReactElement) - Added proper types for event callbacks - Fixed SimplePool.sub typing issues with type assertions **Evolutions:** - Deployment script now automatically updates dependencies to latest versions before deploying - All dependencies updated to latest versions (Next.js 14->16, React 18->19, nostr-tools 1->2, etc.) **Pages affectées:** - package.json - deploy.sh - lib/keyManagement.ts - lib/nostr.ts - lib/nostrRemoteSigner.ts - lib/zapVerification.ts - lib/platformTrackingEvents.ts - lib/sponsoringTracking.ts - lib/articlePublisherHelpersVerification.ts - lib/contentDeliveryVerification.ts - lib/paymentPollingZapReceipt.ts - lib/nostrPrivateMessages.ts - lib/nostrSubscription.ts - lib/nostrZapVerification.ts - lib/markdownRenderer.tsx - components/AuthorFilter.tsx - components/AuthorFilterButton.tsx - components/UserArticlesList.tsx - types/nostr-tools-extended.ts
109 lines
3.0 KiB
TypeScript
109 lines
3.0 KiB
TypeScript
import type { MediaRef } from '@/types/nostr'
|
|
import { getPrimaryNip95Api } from './config'
|
|
|
|
const MAX_IMAGE_BYTES = 5 * 1024 * 1024
|
|
const MAX_VIDEO_BYTES = 45 * 1024 * 1024
|
|
const IMAGE_TYPES = ['image/png', 'image/jpeg', 'image/jpg', 'image/webp']
|
|
const VIDEO_TYPES = ['video/mp4', 'video/webm', 'video/quicktime']
|
|
|
|
function assertBrowser(): void {
|
|
if (typeof window === 'undefined') {
|
|
throw new Error('NIP-95 upload is only available in the browser')
|
|
}
|
|
}
|
|
|
|
function validateFile(file: File): MediaRef['type'] {
|
|
if (IMAGE_TYPES.includes(file.type)) {
|
|
if (file.size > MAX_IMAGE_BYTES) {
|
|
throw new Error('Image exceeds 5MB limit')
|
|
}
|
|
return 'image'
|
|
}
|
|
if (VIDEO_TYPES.includes(file.type)) {
|
|
if (file.size > MAX_VIDEO_BYTES) {
|
|
throw new Error('Video exceeds 45MB limit')
|
|
}
|
|
return 'video'
|
|
}
|
|
throw new Error('Unsupported media type')
|
|
}
|
|
|
|
/**
|
|
* Upload media via NIP-95.
|
|
* This implementation validates size/type then delegates to a pluggable uploader.
|
|
* The actual upload endpoint must be provided via env/config; otherwise an error is thrown.
|
|
*/
|
|
export async function uploadNip95Media(file: File): Promise<MediaRef> {
|
|
assertBrowser()
|
|
const mediaType = validateFile(file)
|
|
|
|
const endpoint = await getPrimaryNip95Api()
|
|
if (!endpoint) {
|
|
throw new Error(
|
|
'NIP-95 upload endpoint is not configured. Please configure a NIP-95 API endpoint in the application settings.'
|
|
)
|
|
}
|
|
|
|
const formData = new FormData()
|
|
formData.append('file', file)
|
|
|
|
let response: Response
|
|
try {
|
|
response = await fetch(endpoint, {
|
|
method: 'POST',
|
|
body: formData,
|
|
})
|
|
} catch (e) {
|
|
const errorMessage = e instanceof Error ? e.message : 'Network error'
|
|
console.error('NIP-95 upload fetch error:', {
|
|
endpoint,
|
|
error: errorMessage,
|
|
fileSize: file.size,
|
|
fileType: file.type,
|
|
})
|
|
throw new Error(`Failed to fetch upload endpoint: ${errorMessage}`)
|
|
}
|
|
|
|
if (!response.ok) {
|
|
let errorMessage = 'Upload failed'
|
|
try {
|
|
const text = await response.text()
|
|
errorMessage = text || `HTTP ${response.status} ${response.statusText}`
|
|
} catch (_e) {
|
|
errorMessage = `HTTP ${response.status} ${response.statusText}`
|
|
}
|
|
console.error('NIP-95 upload response error:', {
|
|
endpoint,
|
|
status: response.status,
|
|
statusText: response.statusText,
|
|
errorMessage,
|
|
fileSize: file.size,
|
|
fileType: file.type,
|
|
})
|
|
throw new Error(errorMessage)
|
|
}
|
|
|
|
let result: { url?: string }
|
|
try {
|
|
result = (await response.json()) as { url?: string }
|
|
} catch (e) {
|
|
const errorMessage = e instanceof Error ? e.message : 'Invalid JSON response'
|
|
console.error('NIP-95 upload JSON parse error:', {
|
|
endpoint,
|
|
error: errorMessage,
|
|
status: response.status,
|
|
})
|
|
throw new Error(`Invalid upload response: ${errorMessage}`)
|
|
}
|
|
|
|
if (!result.url) {
|
|
console.error('NIP-95 upload missing URL:', {
|
|
endpoint,
|
|
response: result,
|
|
})
|
|
throw new Error('Upload response missing URL')
|
|
}
|
|
|
|
return { url: result.url, type: mediaType }
|
|
}
|