Files
tier-maker/backend/scripts/migrate-flat-assets-to-sharded.js

103 lines
2.7 KiB
JavaScript

const fs = require('fs/promises')
const path = require('path')
const {
ensureData,
closePool,
updateImageAssetSrc,
replaceUploadSourceReferences,
} = require('../src/db')
const BACKEND_ROOT = path.join(__dirname, '..')
const ASSETS_ROOT = path.join(BACKEND_ROOT, 'uploads', 'assets')
const FLAT_ASSET_PATTERN = /^\/uploads\/assets\/[^/]+$/
function getShardedAssetSrc(src) {
const filename = path.basename(src || '')
const shardDirectory = filename.slice(0, 2)
if (!filename || shardDirectory.length < 2) return ''
return `/uploads/assets/${shardDirectory}/${filename}`
}
async function moveAssetFile(fromSrc, toSrc) {
const fromPath = path.join(BACKEND_ROOT, fromSrc.replace(/^\//, ''))
const toPath = path.join(BACKEND_ROOT, toSrc.replace(/^\//, ''))
await fs.mkdir(path.dirname(toPath), { recursive: true })
try {
await fs.rename(fromPath, toPath)
return 'moved'
} catch (error) {
if (error?.code !== 'ENOENT') throw error
}
try {
await fs.access(toPath)
return 'already_moved'
} catch (error) {
if (error?.code === 'ENOENT') return 'missing'
throw error
}
}
async function main() {
await ensureData()
let dirEntries = []
try {
dirEntries = await fs.readdir(ASSETS_ROOT, { withFileTypes: true })
} catch (error) {
if (error?.code !== 'ENOENT') throw error
}
const flatAssets = dirEntries
.filter((entry) => entry.isFile())
.map((entry) => ({ src: `/uploads/assets/${entry.name}` }))
.filter((asset) => FLAT_ASSET_PATTERN.test(asset.src || ''))
const summary = {
scanned: flatAssets.length,
migrated: 0,
alreadyMoved: 0,
skipped: 0,
missingFiles: 0,
failed: 0,
updatedRows: 0,
}
for (const asset of flatAssets) {
const nextSrc = getShardedAssetSrc(asset.src)
if (!nextSrc) {
summary.skipped += 1
continue
}
try {
const moveStatus = await moveAssetFile(asset.src, nextSrc)
if (moveStatus === 'missing') {
summary.missingFiles += 1
continue
}
await updateImageAssetSrc({ fromSrc: asset.src, toSrc: nextSrc })
const replaced = await replaceUploadSourceReferences({ fromSrc: asset.src, toSrc: nextSrc })
summary.updatedRows += Number(replaced.updatedRows || 0)
if (moveStatus === 'already_moved') summary.alreadyMoved += 1
else summary.migrated += 1
} catch (error) {
summary.failed += 1
console.error('[migrate-flat-assets-to-sharded] failed:', asset.src, error?.message || error)
}
}
console.log(JSON.stringify(summary, null, 2))
}
main()
.catch((error) => {
console.error(error)
process.exitCode = 1
})
.finally(async () => {
await closePool()
})