"Filterbank" implies "filters", which implies "filtering", or convolution. There is and isn't such equivalence for DFT.
Yes
STFT with maximum window width, maximum hop size, and periodic padding, equals DFT.
Bandpasses are compact in frequency by design, either approximately or exactly. The wider the window in time, the narrower it is in frequency. In continuous time, STFT approaches FT in the limit as window width approaches infinity, yielding pure complex sines in time as convolution kernels, and impulses in frequency. window=np.ones(N) is the discrete equivalent of "infinite window". Visually,
For iDFT, we get a nice interpretation via the one-integral inverse: any given timestep of x is recovered by summing rows of STFT at that timestep (with hop_size=1). This works out because STFT is a decomposition, meaning the sum of its parts equals the total.$^{1}$
The standard inversion of STFT involves undoing the convolution: at each timeshift, undo the multiplication by complex sinusoid, and by the window$^{2}$. iDFT then falls out naturally: the first step is undoing $e^{-j\theta}$ with $e^{j\theta} = 1/e^{-j\theta}$.
1: To be strict, the one-integral inverse doesn't exactly work out for general STFT due to imperfect analyticity, but if bandpasses are impulses, they can't "leak over", so this is the one and only exception. An alternative proof is, DFT_matrix[i, :] = DFT_matrix[:, i].
2: this is actually done at the end, but with the same motivation
No
Above reasoning is actually circular. The one-integral inverse is proven by showing that the filterbank sums to unity in frequency domain, or sum(DFT(filters)) == 1 for all bins. hop_size=N is also proven by showing equivalence with DFT.
Put simply, DFT is not a convolution, nor does its computation involve convolutions. The very notion of "bandpass" is defined in frequency domain, so "why is DFT made of bandpass filters" is sort of like "why are molecules made of atoms".
In detail, each coefficient of DFT is an inner product that collapses time for each frequency. Each coefficient of one convolution is an inner product that collapses time for each time shift, and the total output is all such time shifts. With hop_size=N, the two are equivalent: each one convolution is only a single coefficient, and the number of convolutions = the number of DFT frequencies. Note, this last equality, n_fft=N, was implicitly assumed throughout the answer: it's the smallest n_fft that satisfies NOLA with hop_size=N.
While DFT != convolution, DFT certainly enables convolution. In trying to visualize the convolution theorem (why "mult in freq" $\Leftrightarrow$ "conv in time"), I found the linearity and time-shift properties to be at its core, a nice view on why convolutions are "the" LTI operators. Along the way I also thought of DFT as convolutions, but I both forgot what it was and it was in a very indirect sense.
Other
Re: invertibility If the goal is to understand why DFT is invertible, there's no need to invoke filtering at all. Some explanations: (1) the DFT is a full rank matrix, $=$ invertible -- (2) the DFT is equivalent to solving a system of $N$ equations.
Further reading: I've not actually read these but they seem relevant: DSP related, Wireless Pi.
Note, this answer looks at STFT as bandpass convolutions / time-frequency transform, rather than "windowed Fourier transform". It is the "truer" interpretation, and more relevant here. If unfamiliar, the CWT visual at top of this answer may help.
Code demo
Standard STFT computes for real x so I show it for rDFT, but full STFT will work for any x. Note, scipy.stft lacks periodic padding.
import numpy as np
from ssqueezepy import stft
N = 64
x = np.random.randn(N)
w = np.ones(N)
ckw = dict(window=w, n_fft=N, padtype='wrap')
Sx = stft(x, **ckw, hop_len=1)
rdft = np.fft.rfft(x)
validate as DFT (this is same as hop_size==N)
assert np.allclose(rdft, Sx[:, 0])
validate as one-integral inverse; non-(Nyquist & DC) need adjusting per .real
Sx[1:-1] *= 2
x_inv = Sx.sum(axis=0).real / N # iDFT does / N
assert np.allclose(x_inv, x)
validate explicitly for hop_size==N
Sx = stft(x, **ckw, hop_len=N).squeeze()
assert np.allclose(rdft, Sx)