Files
TypeWords/scripts/deploy-oss.js
2025-09-16 05:25:48 +00:00

142 lines
4.0 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import OSS from 'ali-oss'
import fs from 'fs'
import path from 'path'
import Core from '@alicloud/pop-core'
const {
OSS_REGION,
OSS_KEY_ID,
OSS_KEY_SECRET,
OSS_BUCKET,
CDN_DOMAIN
} = process.env
if (!OSS_REGION || !OSS_KEY_ID || !OSS_KEY_SECRET || !OSS_BUCKET || !CDN_DOMAIN) {
console.error('❌ 缺少必要的环境变量,请检查 GitHub Secrets 配置')
process.exit(1)
}
const client = new OSS({
region: OSS_REGION,
accessKeyId: OSS_KEY_ID,
accessKeySecret: OSS_KEY_SECRET,
bucket: OSS_BUCKET
})
const cdnClient = new Core({
accessKeyId: OSS_KEY_ID,
accessKeySecret: OSS_KEY_SECRET,
endpoint: 'https://cdn.aliyuncs.com',
apiVersion: '2018-05-10'
})
// 遍历 dist 目录,统计文件
function getAllFiles(dir, fileList = []) {
const files = fs.readdirSync(dir)
for (const file of files) {
const filePath = path.join(dir, file)
const stat = fs.statSync(filePath)
if (stat.isDirectory()) {
getAllFiles(filePath, fileList)
} else {
fileList.push(filePath)
}
}
return fileList
}
// 上传文件,显示进度,可跳过指定目录
/**
* 上传文件并清理远端多余文件
* @param files 本地文件完整路径列表
* @param localBase 本地基准路径
* @param ignoreDirs 相对 localBase 的目录名数组,上传时跳过,删除远端时保留
*/
async function uploadFilesWithClean(files, localBase = './dist', ignoreDirs = []) {
// 1⃣ 过滤掉忽略的目录
const filteredFiles = files.filter(file => {
const relativePath = path.relative(localBase, file)
const topDir = relativePath.split(path.sep)[0]
return !ignoreDirs.includes(topDir)
})
// 2⃣ 获取远端已有文件列表
console.log('📄 获取远端文件列表...')
let remoteFiles = []
let marker = ''
do {
const result = await client.list({
prefix: '',
'max-keys': 1000,
marker,
})
if (result.objects) {
remoteFiles.push(...result.objects.map(f => f.name))
}
marker = result.nextMarker || ''
} while (marker)
// 3⃣ 上传文件
const total = filteredFiles.length
let count = 0
const uploadedFiles = []
for (const file of filteredFiles) {
const relativePath = path.relative(localBase, file)
const remotePath = relativePath.split(path.sep).join('/') // POSIX 路径
await client.put(remotePath, file)
uploadedFiles.push(remotePath)
count++
const percent = ((count / total) * 100).toFixed(1)
process.stdout.write(`\r📤 上传进度: ${count}/${total} (${percent}%) ${remotePath} `)
}
console.log('\n✅ 文件上传完成')
// 4⃣ 删除远端多余文件(远端存在但本地未上传),同时保留 ignoreDirs
const toDelete = remoteFiles.filter(f => {
const topDir = f.split('/')[0]
return !uploadedFiles.includes(f) && !ignoreDirs.includes(topDir)
})
if (toDelete.length) {
console.log('🗑 删除远端多余文件:', toDelete)
// 分批删除,防止数量过多
const batchSize = 1000
for (let i = 0; i < toDelete.length; i += batchSize) {
const batch = toDelete.slice(i, i + batchSize)
await client.deleteMulti(batch)
}
console.log('✅ 多余文件删除完成')
} else {
console.log(' 无需删除远端文件')
}
}
// 刷新 CDN
async function refreshCDN() {
console.log('🔄 刷新 CDN 缓存...')
const params = {
ObjectPath: `https://${CDN_DOMAIN}/`,
ObjectType: 'Directory'
}
const requestOption = {method: 'POST'}
const result = await cdnClient.request('RefreshObjectCaches', params, requestOption)
console.log('✅ CDN 刷新完成:', result)
}
async function main() {
const files = getAllFiles('./dist')
console.log(`📁 共找到 ${files.length} 个文件,开始上传...`)
// await uploadFilesWithClean(files, './dist', ['dicts', 'sound', 'libs'])
await uploadFilesWithClean(files, './dist', ['libs'])
await refreshCDN()
}
main().catch(err => {
console.error('❌ 部署失败:', err)
process.exit(1)
})