Switch a fan on for 5 seconds and then turn off

Instead of toggling the relayState when a button is pressed, set it to HIGH (assuming the relay is activated with a HIGH signal) and 'start a timer'.

The below code is based on your code; it gives the fleibility to use active HIGH or active LOW relay.

// defines if relay is active when a the pin is set HIGH; change HIGH to LOW if the relay activates with a LOW signal
#define ACTIVE HIGH

int buttonPin = 2;
int relayPin = 3;
int relayState = HIGH;
int buttonState; //record the current button state
int lastButtonState = LOW; // record the last button state

// changed long to unsigned long; millis() based timings should use unsigned variables
unsigned long lastDebounceTime = 0;
unsigned long debounceDelay = 50; // eliminate debounce time

// duration that relay must be on
const unsigned long duration = 5000;

void setup()
{
  pinMode(buttonPin, INPUT);
  pinMode(relayPin, OUTPUT);

  digitalWrite(relayPin, relayState); // configure the initial state of relay

}

void loop()
{
  
  static unsigned long startTime;

  if (relayState != ACTIVE)
  {
    int reading = digitalRead(buttonPin); //read the value of button

    //once detects change of state, record time
    if (reading != lastButtonState)
    {
      lastDebounceTime = millis();
    }

    // wait for 50ms to evaluate if it is the same state as last state
    // if different, change the button state
    // if the state of button is high(pressed), change the state of relay
    if ((millis() - lastDebounceTime) > debounceDelay)
    {
      if (reading != buttonState)
      {
        buttonState = reading;

        if (buttonState == HIGH)
        {
          // 'start' a timer
          startTime = millis();
          // set the relay state to active
          relayState = ACTIVE;
        }
      }
    }
    //change the last state of button
    lastButtonState = reading;
  }
  else
  {
    // check if time is over
    if (millis() - startTime > duration)
    {
      // set the relay state back to inactive
      relayState = !ACTIVE;
    }
  }

  digitalWrite(relayPin, relayState);
}

I have also fixed a minor issue in your original code regarding long / unsigned long.

Compiles, not tested.

Note:
relayState is also used as a flag to check if the relay is active; if the relay is active, the button is not checked.