Source: config/migrations/20260329-avatar-strip-https.js

/**
 * 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`);
};