"use strict"; /* * 共有ドライブ上の Pleasanter_DB_Backup 配下から、指定ホスト名の最新バックアップ * (同一タイムスタンプの分割ファイル一式)を特定する。restore.js が使用する。 * 新しい日付フォルダから順に探索し、対象ホスト名のファイルが無ければ古いフォルダへ遡る * (多サーバー運用で、その日はまだ対象サーバーのバックアップが無い場合に対応するため)。 */ function parseBackupFileName(fileName, hostname, prefix) { const escapedHost = hostname.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); const re = new RegExp(`^\\[${escapedHost}\\]${prefix}-(\\d{8}-\\d{6})\\.dmp(\\d{3})$`); const m = re.exec(fileName); if (!m) return null; return { timestamp: m[1], partIndex: Number(m[2]) }; } async function findLatestBackupFileSet( token, sharedriveId, backupFolderId, hostname, drive, logger, prefix = "postgres" ) { const dateFolders = (await drive.listChildren(token, sharedriveId, backupFolderId, logger)) .filter((f) => f.fileType === "FOLDER" && /^\d{8}$/.test(f.fileName)) .sort((a, b) => (a.fileName < b.fileName ? 1 : a.fileName > b.fileName ? -1 : 0)); for (const folder of dateFolders) { const children = await drive.listChildren(token, sharedriveId, folder.fileId, logger); const matched = children .map((f) => { const parsed = parseBackupFileName(f.fileName, hostname, prefix); return parsed ? { file: f, ...parsed } : null; }) .filter(Boolean); if (matched.length === 0) continue; const latestTimestamp = matched.reduce( (max, m) => (m.timestamp > max ? m.timestamp : max), matched[0].timestamp ); const files = matched .filter((m) => m.timestamp === latestTimestamp) .sort((a, b) => a.partIndex - b.partIndex) .map((m) => m.file); return { dateFolderName: folder.fileName, timestamp: latestTimestamp, files }; } return null; } function parseConfigArchiveName(fileName, hostname) { const escapedHost = hostname.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); const re = new RegExp(`^\\[${escapedHost}\\]config-(\\d{8}-\\d{6})\\.tar\\.gz$`); const m = re.exec(fileName); if (!m) return null; return { timestamp: m[1] }; } // 設定ファイルアーカイブ([hostname]config-.tar.gz)は分割されない単一ファイルのため、 // findLatestBackupFileSetとは別に、日付フォルダを新しい順に探索して最初に見つかった1件を返す。 async function findLatestConfigArchive(token, sharedriveId, backupFolderId, hostname, drive, logger) { const dateFolders = (await drive.listChildren(token, sharedriveId, backupFolderId, logger)) .filter((f) => f.fileType === "FOLDER" && /^\d{8}$/.test(f.fileName)) .sort((a, b) => (a.fileName < b.fileName ? 1 : a.fileName > b.fileName ? -1 : 0)); for (const folder of dateFolders) { const children = await drive.listChildren(token, sharedriveId, folder.fileId, logger); const matched = children .map((f) => { const parsed = parseConfigArchiveName(f.fileName, hostname); return parsed ? { file: f, ...parsed } : null; }) .filter(Boolean) .sort((a, b) => (a.timestamp < b.timestamp ? 1 : a.timestamp > b.timestamp ? -1 : 0)); if (matched.length === 0) continue; return { dateFolderName: folder.fileName, timestamp: matched[0].timestamp, file: matched[0].file }; } return null; } module.exports = { findLatestBackupFileSet, parseBackupFileName, findLatestConfigArchive, parseConfigArchiveName };