| 156 | } |
| 157 | |
| 158 | async _updateExpirations(sid: string, sess: any, updateDbExp = true) { |
| 159 | const exp = this._expirations.get(sid) || {}; |
| 160 | clearTimeout(exp.timeout); |
| 161 | // @ts-ignore |
| 162 | const {cookie: {expires} = {}} = sess || {}; |
| 163 | if (expires) { |
| 164 | const sessExp = new Date(expires).getTime(); |
| 165 | if (updateDbExp) exp.db = sessExp; |
| 166 | exp.real = Math.max(exp.real || 0, exp.db || 0, sessExp); |
| 167 | const now = Date.now(); |
| 168 | if (exp.real <= now) return await this._destroy(sid); |
| 169 | // If reading from the database, update the expiration with the latest value from touch() so |
| 170 | // that touch() appears to write to the database every time even though it doesn't. |
| 171 | if (typeof expires === 'string') sess.cookie.expires = new Date(exp.real).toJSON(); |
| 172 | // Schedule cleanup when the session is expected to expire. When the timeout fires, check |
| 173 | // the in-memory expiry first — touch() may have extended it without rescheduling the timeout |
| 174 | // (e.g., if touch's clearTimeout raced with the timer on a slow system). If the session was |
| 175 | // extended, reschedule instead of reading from the DB which may return stale cached data. |
| 176 | exp.timeout = setTimeout(() => { |
| 177 | const currentExp = this._expirations.get(sid); |
| 178 | if (currentExp && currentExp.real > Date.now()) { |
| 179 | // Expiry was extended (e.g., by touch). Reschedule. |
| 180 | currentExp.timeout = setTimeout(() => this._get(sid), currentExp.real - Date.now()); |
| 181 | return; |
| 182 | } |
| 183 | // Use this._get(), not this._destroy(), to query the DB for the latest expiration in case |
| 184 | // multiple Etherpad instances share the database. (Caveat: client-side DB caching could |
| 185 | // still cause premature deletion if the cache returns a stale expiration time.) |
| 186 | this._get(sid); |
| 187 | }, exp.real - now); |
| 188 | this._expirations.set(sid, exp); |
| 189 | } else { |
| 190 | this._expirations.delete(sid); |
| 191 | } |
| 192 | return sess; |
| 193 | } |
| 194 | |
| 195 | async _write(sid: string, sess: any) { |
| 196 | await DB.set(`sessionstorage:${sid}`, sess); |