add avatar feed database cleanup settings and purge function

This commit is contained in:
pa
2026-03-16 21:52:34 +09:00
parent a8a14ae901
commit 357ac1a8bb
5 changed files with 241 additions and 1 deletions
+1
View File
@@ -839,6 +839,7 @@ export const useAuthStore = defineStore('Auth', () => {
*/
async function loginComplete() {
await database.initUserTables(userStore.currentUser.id);
advancedSettingsStore.runAvatarAutoCleanup(userStore.currentUser.id);
watchState.isLoggedIn = true;
AppApi.CheckGameRunning(); // restore state from hot-reload
}
+105
View File
@@ -60,6 +60,8 @@ export const useAdvancedSettingsStore = defineStore('AdvancedSettings', () => {
const showConfirmationOnSwitchAvatar = ref(false);
const gameLogDisabled = ref(false);
const sqliteTableSizes = ref({});
const avatarAutoCleanup = ref('Off');
const purgeInProgress = ref(false);
const ugcFolderPath = ref('');
const autoDeleteOldPrints = ref(false);
const notificationOpacity = ref(100);
@@ -109,6 +111,7 @@ export const useAdvancedSettingsStore = defineStore('AdvancedSettings', () => {
progressPieFilterConfig,
showConfirmationOnSwitchAvatarConfig,
gameLogDisabledConfig,
avatarAutoCleanupConfig,
ugcFolderPathConfig,
autoDeleteOldPrintsConfig,
notificationOpacityConfig,
@@ -157,6 +160,7 @@ export const useAdvancedSettingsStore = defineStore('AdvancedSettings', () => {
false
),
configRepository.getBool('VRCX_gameLogDisabled', false),
configRepository.getString('VRCX_avatarAutoCleanup', 'Off'),
configRepository.getString('VRCX_userGeneratedContentPath', ''),
configRepository.getBool('VRCX_autoDeleteOldPrints', false),
configRepository.getFloat('VRCX_notificationOpacity', 100),
@@ -203,6 +207,7 @@ export const useAdvancedSettingsStore = defineStore('AdvancedSettings', () => {
showConfirmationOnSwitchAvatar.value =
showConfirmationOnSwitchAvatarConfig;
gameLogDisabled.value = gameLogDisabledConfig;
avatarAutoCleanup.value = avatarAutoCleanupConfig;
ugcFolderPath.value = ugcFolderPathConfig;
autoDeleteOldPrints.value = autoDeleteOldPrintsConfig;
notificationOpacity.value = notificationOpacityConfig;
@@ -524,6 +529,101 @@ export const useAdvancedSettingsStore = defineStore('AdvancedSettings', () => {
);
}
async function setAvatarAutoCleanup(value) {
avatarAutoCleanup.value = value;
await configRepository.setString('VRCX_avatarAutoCleanup', value);
}
/**
* @param {number|null} days - Number of days to keep. Null means delete all.
*/
async function purgeAvatarFeedData(days) {
let cutoffDate = null;
if (days !== null) {
const cutoff = new Date();
cutoff.setDate(cutoff.getDate() - days);
cutoffDate = cutoff.toJSON();
}
purgeInProgress.value = true;
const msgBox = toast.warning(
t(
'view.settings.advanced.advanced.database_cleanup.purge_in_progress'
),
{ duration: Infinity }
);
try {
await database.purgeAvatarFeedData(cutoffDate);
await database.vacuum();
toast.dismiss(msgBox);
toast.success(
t(
'view.settings.advanced.advanced.database_cleanup.purge_complete'
)
);
// Brief delay before restart to show success message
await new Promise((resolve) =>
setTimeout(resolve, 1500)
);
VRCXUpdaterStore.restartVRCX(false);
} catch (err) {
console.error(err);
toast.dismiss(msgBox);
toast.error(t('view.settings.advanced.advanced.database_cleanup.purge_failed', { error: err }));
} finally {
purgeInProgress.value = false;
}
}
/**
* Run auto-cleanup on startup if configured and enough time has passed.
* Reads config directly from configRepository to avoid race condition
* with initAdvancedSettings not having completed yet.
* @param {string} userId - Current user ID for per-user cleanup tracking.
*/
async function runAvatarAutoCleanup(userId) {
const cleanupSetting = await configRepository.getString(
'VRCX_avatarAutoCleanup',
'Off'
);
if (cleanupSetting === 'Off') return;
const configKey = `VRCX_lastAvatarCleanupDate_${userId}`;
const lastCleanupStr = await configRepository.getString(
configKey,
''
);
const now = new Date();
if (lastCleanupStr) {
const lastCleanup = new Date(lastCleanupStr);
const daysSinceLastCleanup =
(now - lastCleanup) / (1000 * 60 * 60 * 24);
if (daysSinceLastCleanup < 7) return;
}
const days = parseInt(cleanupSetting, 10);
if (isNaN(days) || days <= 0) return;
const cutoff = new Date();
cutoff.setDate(cutoff.getDate() - days);
const cutoffDate = cutoff.toJSON();
try {
await database.purgeAvatarFeedData(cutoffDate);
await configRepository.setString(
configKey,
now.toJSON()
);
console.log(
`Auto-cleaned avatar feed data older than ${days} days`
);
} catch (err) {
console.error('Avatar auto-cleanup failed:', err);
}
}
async function setSaveInstanceEmoji() {
saveInstanceEmoji.value = !saveInstanceEmoji.value;
await configRepository.setBool(
@@ -1030,6 +1130,8 @@ export const useAdvancedSettingsStore = defineStore('AdvancedSettings', () => {
showConfirmationOnSwitchAvatar,
gameLogDisabled,
sqliteTableSizes,
avatarAutoCleanup,
purgeInProgress,
ugcFolderPath,
currentUserInventory,
autoDeleteOldPrints,
@@ -1069,6 +1171,9 @@ export const useAdvancedSettingsStore = defineStore('AdvancedSettings', () => {
setProgressPieFilter,
setShowConfirmationOnSwitchAvatar,
setGameLogDisabled,
setAvatarAutoCleanup,
purgeAvatarFeedData,
runAvatarAutoCleanup,
setUGCFolderPath,
cropPrintsChanged,
setAutoDeleteOldPrints,