/**
* Migration: avatar → array
*
* Converts Member.Data.avatar from a single file URL string to an array
* containing just the filename. If the stored URL already contains a `?`
* query string that part is kept; otherwise `?date=<iso-date>` is appended
* using the current wall-clock time so clients can bust their caches.
*
* Before: "avatar.jpg" or "https://…/UID/avatar/avatar.jpg"
* After: ["avatar.jpg?date=2026-03-29"] (filename only, array)
*
* Remove `export const disabled = true` to enable this migration.
*/
export const id = '20260329-avatar-strip-https';
export const name = 'Strip URL from Member.Data.avatar and keep only filename';
/** Set to false (or delete this line) when ready to apply */
export const disabled = false;
/** Set to false to actually write changes */
export const DRY_RUN = false;
/**
* @param {{ query: Function, transaction: Function, UUID2hex: Function, HEX2uuid: Function }} api
*/
const newAvatar=(raw)=>{
// Strip any path prefix — keep only the filename (last path segment, before any '?')
const pathPart = raw.split('?')[0];
const queryPart = raw.includes('?') ? raw.slice(raw.indexOf('?')) : `?t=${Date.now()}`;
const filename = pathPart.split('/').pop().replace(/avatar%2[F,f]/, ''); // Handle URL-encoded slash in some stored URLs
const dateSuffix = queryPart ?? `?date=${new Date().toISOString().slice(0, 10)}`;
return `${filename}${dateSuffix}`;
};
export const migrate = async ({ query }) => {
const rows = await query(
`SELECT UID, Data FROM Member WHERE JSON_VALUE(Data, '$.avatar') LIKE 'https:%' OR JSON_VALUE(Data, '$.avatar_white') LIKE 'https:%' OR JSON_VALUE(Data, '$.avatar_black') LIKE 'https:%'`,
[],
{ cast: ['json'] }
);
for (const row of rows) {
const Data = row.Data;
if(Data.avatar && typeof Data.avatar === 'string' && Data.avatar.startsWith('https:')) {
Data.avatar = newAvatar(Data.avatar);
}
if(Data.avatar_white && typeof Data.avatar_white === 'string' && Data.avatar_white.startsWith('https:')) {
Data.avatar_white = newAvatar(Data.avatar_white);
}
if(Data.avatar_black && typeof Data.avatar_black === 'string' && Data.avatar_black.startsWith('https:')) {
Data.avatar_black = newAvatar(Data.avatar_black);
}
if (DRY_RUN) {
console.log('[DRY RUN] avatar migration:', { UID: row.UID, before: row.Data, after: Data });
} else {
const updated = { ...row.Data, avatar: Data.avatar, avatar_white: Data.avatar_white, avatar_black: Data.avatar_black };
await query(`UPDATE Member SET Data = ? WHERE UID = ?`, [JSON.stringify(Data), row.UID]);
}
}
if (DRY_RUN) console.log(`[DRY RUN] avatar migration: ${rows.length} row(s) would be updated`);
};