94 lines
3.8 KiB
JavaScript
94 lines
3.8 KiB
JavaScript
"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-<timestamp>.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 };
|