mirror of
https://github.com/spacedriveapp/spacedrive.git
synced 2025-12-11 20:15:30 +01:00
* Update rspc, prisma-client-rust, axum and tanstack-query - Deleted some unused examples and fully commented out frontend code - Implement many changes required due to the updates - Update most rust dependencies * Re-enable p2p * Fix server * Auto format * Fix injected script format - Update some github actions - Update pnpm lock file * Fix devtools showing up when app opens - Fix million complaining about Sparkles component * Fix sd-server * Fix and improve thumbnails rendering - Fix core always saying a new thumbnail was generated even for files that it skiped thumbnail generation - Rewrite FileThumb and improve related components * Ignore tmp files when running prettier * Improve FileThumb component performance - Rework useExplorerDraggable and useExplorerItemData hooks due to reduce unecessary re-renders * More fixes for thumb component - A couple of minor performance improvements to frontend code * auto format * Fix Thumbnail and QuickPreview * Fix logic for when to show 'fail to load original' error message in QuickPreview - Updated prisma-client-rust, libp2p, tauri, tauri-specta, rspc and hyper * Fix type checking - Format scripts * Add script prettier config * Fix serde missing feature - Use rust-libp2p spacedrive fork again - Update rspc * Autoformat + fix pnpm lock * Fix thumbnail first load again * Autoformat * autoformat * Fix rust-libp2p fork url again? * Remove usePathsInfiniteQuery hook * Update tauri 2.0.6
161 lines
4.4 KiB
JavaScript
161 lines
4.4 KiB
JavaScript
import * as fs from 'node:fs/promises'
|
|
import { dirname, join as joinPath } from 'node:path'
|
|
import { env } from 'node:process'
|
|
import { fileURLToPath } from 'node:url'
|
|
|
|
import { getSystemProxy } from 'os-proxy-config'
|
|
import { Agent, fetch, Headers, ProxyAgent } from 'undici'
|
|
|
|
const CONNECT_TIMEOUT = 5 * 60 * 1000
|
|
const __debug = env.NODE_ENV === 'debug'
|
|
const __offline = env.OFFLINE === 'true'
|
|
const __filename = fileURLToPath(import.meta.url)
|
|
const __dirname = dirname(__filename)
|
|
const cacheDir = joinPath(__dirname, '.tmp')
|
|
|
|
/** @type {Agent.Options} */
|
|
const agentOpts = {
|
|
allowH2: !!env.HTTP2,
|
|
connect: { timeout: CONNECT_TIMEOUT },
|
|
connectTimeout: CONNECT_TIMEOUT,
|
|
autoSelectFamily: true,
|
|
}
|
|
|
|
const { proxyUrl } = (await getSystemProxy()) ?? {}
|
|
const dispatcher = proxyUrl
|
|
? new ProxyAgent({
|
|
...agentOpts,
|
|
proxyTls: { timeout: CONNECT_TIMEOUT },
|
|
requestTls: { timeout: CONNECT_TIMEOUT },
|
|
uri: proxyUrl,
|
|
})
|
|
: new Agent(agentOpts)
|
|
|
|
await fs.mkdir(cacheDir, { recursive: true, mode: 0o751 })
|
|
|
|
/**
|
|
* @param {string} resource
|
|
* @param {Headers} [headers]
|
|
* @returns {Promise<null | {data: Buffer, header: [string, string] | undefined}>}
|
|
*/
|
|
async function getCache(resource, headers) {
|
|
/** @type {Buffer | undefined} */
|
|
let data
|
|
/** @type {[string, string] | undefined} */
|
|
let header
|
|
|
|
// Don't cache in CI
|
|
if (env.CI === 'true' || env.NO_CACHE === 'true') return null
|
|
|
|
if (headers)
|
|
resource += Array.from(headers.entries())
|
|
.filter(([name]) => name !== 'If-None-Match' && name !== 'If-Modified-Since')
|
|
.flat()
|
|
.join(':')
|
|
try {
|
|
const cache = JSON.parse(
|
|
await fs.readFile(joinPath(cacheDir, Buffer.from(resource).toString('base64url')), {
|
|
encoding: 'utf8',
|
|
})
|
|
)
|
|
if (cache && typeof cache === 'object') {
|
|
if (cache.etag && typeof cache.etag === 'string') {
|
|
header = ['If-None-Match', cache.etag]
|
|
} else if (cache.modifiedSince && typeof cache.modifiedSince === 'string') {
|
|
header = ['If-Modified-Since', cache.modifiedSince]
|
|
}
|
|
|
|
if (cache.data && typeof cache.data === 'string')
|
|
data = Buffer.from(cache.data, 'base64')
|
|
}
|
|
} catch (error) {
|
|
if (__debug) {
|
|
console.warn(`CACHE MISS: ${resource}`)
|
|
console.error(error)
|
|
}
|
|
}
|
|
|
|
return data ? { data, header } : null
|
|
}
|
|
|
|
/**
|
|
* @param {import('undici').Response} response
|
|
* @param {string} resource
|
|
* @param {Buffer} [cachedData]
|
|
* @param {Headers} [headers]
|
|
* @returns {Promise<Buffer>}
|
|
*/
|
|
async function setCache(response, resource, cachedData, headers) {
|
|
const data = Buffer.from(await response.arrayBuffer())
|
|
|
|
// Don't cache in CI
|
|
if (env.CI === 'true') return data
|
|
|
|
const etag = response.headers.get('ETag') || undefined
|
|
const modifiedSince = response.headers.get('Last-Modified') || undefined
|
|
if (headers)
|
|
resource += Array.from(headers.entries())
|
|
.filter(([name]) => name !== 'If-None-Match' && name !== 'If-Modified-Since')
|
|
.flat()
|
|
.join(':')
|
|
|
|
if (response.status === 304 || (response.ok && data.length === 0)) {
|
|
// Cache hit
|
|
if (!cachedData) throw new Error('Empty cache hit ????')
|
|
return cachedData
|
|
}
|
|
|
|
try {
|
|
await fs.writeFile(
|
|
joinPath(cacheDir, Buffer.from(resource).toString('base64url')),
|
|
JSON.stringify({
|
|
etag,
|
|
modifiedSince,
|
|
data: data.toString('base64'),
|
|
}),
|
|
{ mode: 0o640, flag: 'w+' }
|
|
)
|
|
} catch (error) {
|
|
if (__debug) {
|
|
console.warn(`CACHE WRITE FAIL: ${resource}`)
|
|
console.error(error)
|
|
}
|
|
}
|
|
|
|
return data
|
|
}
|
|
|
|
/**
|
|
* @param {URL | string} resource
|
|
* @param {Headers?} [headers]
|
|
* @param {boolean} [preferCache]
|
|
* @returns {Promise<Buffer>}
|
|
*/
|
|
export async function get(resource, headers, preferCache) {
|
|
if (headers == null) headers = new Headers()
|
|
if (resource instanceof URL) resource = resource.toString()
|
|
|
|
const cache = await getCache(resource, headers)
|
|
if (__offline) {
|
|
if (cache?.data == null)
|
|
throw new Error(`OFFLINE MODE: Cache for request ${resource} doesn't exist`)
|
|
return cache.data
|
|
}
|
|
if (preferCache && cache?.data != null) return cache.data
|
|
|
|
if (cache?.header) headers.append(...cache.header)
|
|
|
|
if (__debug) console.log(`Downloading ${resource} ${cache?.data ? ' (cached)' : ''}...`)
|
|
const response = await fetch(resource, { dispatcher, headers })
|
|
|
|
if (!response.ok) {
|
|
if (cache?.data) {
|
|
if (__debug) console.warn(`CACHE HIT due to fail: ${resource} ${response.statusText}`)
|
|
return cache.data
|
|
}
|
|
throw new Error(response.statusText)
|
|
}
|
|
|
|
return await setCache(response, resource, cache?.data, headers)
|
|
}
|