[SOLVED] Differences in asset URLs depending on environment

I have…

I’m submitting a…

  • Regression (a behavior that stopped working in a new release)
  • [x ] Bug report
  • Performance issue
  • Documentation issue or request

Current behavior

Hi, I have a strange problem, I’m running a self-hosted Squidex version 7.18.0.0.

In our stage environment I created an app with lots of assets, these assets were uploaded from an external application using a script. The filenames for assets can look like this:

plejd_product_sheet_LED-75_DE_CH

Asset URL
https://myDevSquidex/api/assets/brand-guide/7253fcbd-d7e2-4dbe-b215-cc750af04fb3/plejd_product_sheet_LED-75_DE_CH.pdf?version=0

Everything looks fine and so on. I make a backup of my app and restore it in prod, but suddenly my asset URL is in lowercase and always with “-”:

https://myProdSquidex/api/assets/brand-guide/7253fcbd-d7e2-4dbe-b215-cc750af04fb3/plejd-product-sheet-led-75-de-ch.pdf?version=0

Why does this happen? I tested making a new backup of the application, this time I made the backup in prod and restored it on dev, but on dev the URLs still have the correct casing and _ instead of -, while in prod they’re always lowercase with -.

Expected behavior

I want the app to be imported with the correct urls

App Name:

  • [ x] Self hosted with docker
  • Self hosted with IIS
  • Self hosted with other version
  • Cloud version

This part of the URL is called slug. It is derived from the app name and actually irrelevant. It is only added there because some people wanted to have nicer URLs for SEO purposes. Only the ID is actually relevant

Now, im linking this to a cdn and the casing does matter if i only want to use the slug in for my products. Take this one as an example

https://mysquidex.com/api/assets/brand-guide/4aced40e-0845-434f-8c08-71bc366a9aa6/plejd_product_sheet_LED-75_PL.pdf?version=1 works

https://mysquidex.com/api/assets/brand-guide/plejd_product_sheet_LED-75_PL.pdf?version=1 works

https://mysquidex.com/api/assets/brand-guide/plejd_product_sheet_led-75_pl.pdf?version=1 does not work

Now in my cdn

https://brand-cdn.plejd.com/plejd_product_sheet_LED-75_PL.pdf works

https://brand-cdn.plejd.com/plejd_product_sheet_led-75_PL.pdf does not work.

How ever, I discovered the issue is not really a discrepancy in environments, it’s the “generate slug” button that works different when you press it as a user or if you run it via script.

example (this file does not exist btw)
if I upload a file with the name “this_IS_MY-file.pdf” the url will sometimes (not sure why it’s just sometimes) become something like:

https://mysquidex.com/api/assets/brand-guide/4aced40e-0845-434f-8c08-71bc366a9aa6/this-is-my-file.pdf?version=1 (yes with “-” and lowecase)

If i press “generate slug” it will pickup the “_” so now the url is

https://mysquidex.com/api/assets/brand-guide/4aced40e-0845-434f-8c08-71bc366a9aa6/this_is_my-file.pdf?version=1 how ever, the casing will still be lowercase but if i run it with my script that runs “generate slug” for all assets it will also pickup the casing

https://mysquidex.com/api/assets/brand-guide/4aced40e-0845-434f-8c08-71bc366a9aa6/this_IS_MY-file.pdf?version=1

This is my script for generating slugs on all assets (no weirdness here?)

import 'dotenv/config';
import { getAccessToken, getAllSquidexAssets } from './helper.mjs';

const SQUIDEX_APP_NAME = process.env.SQUIDEX_APP_NAME;
const SQUIDEX_URL = process.env.SQUIDEX_URL;

/**
 * Updates the slug for a given asset.
 * This function replicates the logic found in `uploadFiles.mjs`.
 * @param {string} assetId The ID of the asset to update.
 * @param {string} newSlug The new slug to set for the asset.
 * @param {string} token The authorization token for the Squidex API.
 */
async function updateAssetSlug(assetId, newSlug, token) {
  const url = new URL(`/api/apps/${SQUIDEX_APP_NAME}/assets/${assetId}`, SQUIDEX_URL);
  const body = { slug: newSlug };

  try {
    const response = await fetch(url, {
      method: 'PUT',
      body: JSON.stringify(body),
      headers: {
        'Content-Type': 'application/json',
        Authorization: `Bearer ${token}`,
      },
    });

    if (response.ok) {
      console.log(`Successfully updated slug for asset ID ${assetId} to: ${newSlug}`);
    } else {
      console.error(`Error updating slug for asset ID ${assetId}: ${response.status} ${await response.text()}`);
    }
  } catch (error) {
    console.error(`Error updating slug for asset ID ${assetId}:`, error);
  }
}

async function main() {
  console.log('Starting script to generate slugs for all Squidex assets...');

  const token = await getAccessToken();
  if (!token) {
    console.error('Failed to get access token. Exiting.');
    return;
  }

  console.log('Fetching all assets from Squidex...');
  const allAssets = await getAllSquidexAssets();
  console.log(`Found ${allAssets.length} assets to process.`);

  let updatedCount = 0;
  for (const asset of allAssets) {
    // We only care about assets (files), not folders.
    if (asset.type === 'Folder' || !asset.fileName) {
      continue;
    }

    if (asset.slug !== asset.fileName) {
      console.log(`Asset ${asset.id} ('${asset.fileName}') needs slug update. Current slug: '${asset.slug}'.`);
      await updateAssetSlug(asset.id, asset.fileName, token);
      updatedCount++;
    }
  }

  console.log('--------------------');
  console.log('Script finished.');
  if (updatedCount > 0) {
    console.log(`Updated slugs for ${updatedCount} assets.`);
  } else {
    console.log('All asset slugs were already up to date.');
  }
}

main().catch(console.error);

I also made a screen recording, it’s a bit blurry but think it should work anyway. What it describes

  1. Upload a file with uppercase and _ in the name
  2. url becomes all lowercase and -
  3. generate slug will pick up - or _ but not casing
  4. delete the file and upload it again
  5. Run script (the one above) to genereate slug
  6. Show that this does pick up the casing
  7. Bug?

Yes, this should be fixed. But please note that there is no duplicate checks for slugs.

1 Like

Cool! Do you have any idea how long such fix would take? I might need to find some work around, i’ve tried settting up asset scripts that should run when an asset is created/annotated but couldn’t get it working, my best shot right now is using a webhook to a custom script that will run each time someone uploads an asset but it feels a bit hacky

Sorry, I have actually fixed the problem with slug generation.