'use strict'; const crypto = require('node:crypto'), path = require('node:path'), safe = require('safetydance'); function getMountPoint(dataDir) { const output = safe.child_process.execSync(`df --output=target "${dataDir}" | tail -1`, { encoding: 'utf8' }); if (!output) return dataDir; const mountPoint = output.trim(); if (mountPoint === '/') return dataDir; return mountPoint; } exports.up = async function(db) { // use safe() here because this migration failed midway in 7.2.4 await safe(db.runSql('ALTER TABLE apps ADD storageVolumeId VARCHAR(128), ADD FOREIGN KEY(storageVolumeId) REFERENCES volumes(id)')); await safe(db.runSql('ALTER TABLE apps ADD storageVolumePrefix VARCHAR(128)')); await safe(db.runSql('ALTER TABLE apps ADD CONSTRAINT apps_storageVolume UNIQUE (storageVolumeId, storageVolumePrefix)')); const apps = await db.runSql('SELECT * FROM apps WHERE dataDir IS NOT NULL'); for (const app of apps) { const allVolumes = await db.runSql('SELECT * FROM volumes'); console.log(`data-dir (${app.id}): migrating data dir ${app.dataDir}`); const mountPoint = getMountPoint(app.dataDir); const prefix = path.relative(mountPoint, app.dataDir); console.log(`data-dir (${app.id}): migrating to mountpoint ${mountPoint} and prefix ${prefix}`); const volume = allVolumes.find(v => v.hostPath === mountPoint); if (volume) { console.log(`data-dir (${app.id}): using existing volume ${volume.id}`); await db.runSql('UPDATE apps SET storageVolumeId=?, storageVolumePrefix=? WHERE id=?', [ volume.id, prefix, app.id ]); continue; } const id = crypto.randomUUID().replace(/-/g, ''); // to make systemd mount file names more readable const name = `appdata-${id}`; const type = app.dataDir === mountPoint ? 'filesystem' : 'mountpoint'; console.log(`data-dir (${app.id}): creating new volume ${id}`); await db.runSql('INSERT INTO volumes (id, name, hostPath, mountType, mountOptionsJson) VALUES (?, ?, ?, ?, ?)', [ id, name, mountPoint, type, JSON.stringify({}) ]); await db.runSql('UPDATE apps SET storageVolumeId=?, storageVolumePrefix=? WHERE id=?', [ id, prefix, app.id ]); } await db.runSql('ALTER TABLE apps DROP COLUMN dataDir'); }; exports.down = async function(/*db*/) { };