{"version":3,"sources":["../../src/session/redis.ts"],"sourcesContent":["import type { Redis } from 'ioredis';\nimport { PRINCIPAL_NAME_INDEX_NAME, SPRING_SECURITY_CONTEXT, resolveIndexesFor } from './common';\nimport { MapSession } from './map-session';\nimport type { KVRepository, Namespace, Session, SessionRepository } from './types';\n\nconst CREATION_TIME_KEY = 'creationTime';\nconst LAST_ACCESSED_TIME_KEY = 'lastAccessedTime';\nconst MAX_INACTIVE_INTERVAL_KEY = 'maxInactiveInterval';\nconst ATTRIBUTE_PREFIX = 'sessionAttr:';\nconst SESSION_EXPIRES_PREFIX = 'expires:';\n\nclass RedisSession implements Session {\n  private readonly cached: MapSession;\n\n  public isNew: boolean;\n  public delta = new Map<string, string | number | null>();\n  public originalSessionId: string;\n  public originalPrincipalName: string | null;\n  public originalLastAccessTime: number | null = null;\n\n  constructor(cached: MapSession, isNew: boolean) {\n    this.cached = cached;\n    this.isNew = isNew;\n    this.originalSessionId = cached.getId();\n    this.originalPrincipalName = resolveIndexesFor(this);\n\n    if (isNew) {\n      this.delta.set(CREATION_TIME_KEY, cached.getCreationTime());\n      this.delta.set(LAST_ACCESSED_TIME_KEY, cached.getLastAccessedTime());\n      this.delta.set(MAX_INACTIVE_INTERVAL_KEY, cached.getMaxInactiveInterval());\n      this.cached.getAttributeNames().forEach((name) => {\n        const value = this.cached.getAttribute(name);\n        if (value) this.delta.set(this.getSessionAttrNameKey(name), value);\n      });\n    }\n  }\n\n  getSessionAttrNameKey(name: string) {\n    return ATTRIBUTE_PREFIX + name;\n  }\n\n  // override\n  setLastAccessedTime(lastAccessedTime: number) {\n    this.cached.setLastAccessedTime(lastAccessedTime);\n    this.delta.set(LAST_ACCESSED_TIME_KEY, lastAccessedTime);\n  }\n\n  isExpired() {\n    return this.cached.isExpired();\n  }\n\n  getCreationTime() {\n    return this.cached.getCreationTime();\n  }\n\n  getId() {\n    return this.cached.getId();\n  }\n\n  changeSessionId() {\n    return this.cached.changeSessionId();\n  }\n\n  getLastAccessedTime() {\n    return this.cached.getLastAccessedTime();\n  }\n\n  setMaxInactiveInterval(interval: number) {\n    this.cached.setMaxInactiveInterval(interval);\n    this.delta.set(MAX_INACTIVE_INTERVAL_KEY, interval);\n  }\n\n  getMaxInactiveInterval() {\n    return this.cached.getMaxInactiveInterval();\n  }\n\n  getAttribute(name: string) {\n    return this.cached.getAttribute(name);\n  }\n\n  getAttributeNames() {\n    return this.cached.getAttributeNames();\n  }\n\n  setAttribute(name: string, value: string) {\n    this.cached.setAttribute(name, value);\n    this.delta.set(this.getSessionAttrNameKey(name), value);\n  }\n\n  removeAttribute(name: string) {\n    this.cached.removeAttribute(name);\n    this.delta.set(this.getSessionAttrNameKey(name), null);\n  }\n}\n\ninterface RedisSessionExpirationStore {\n  save(session: RedisSession): Promise<void>;\n  remove(sessionId: string): Promise<void>;\n  rename(session: RedisSession): Promise<void>;\n  cleanupExpiredSessions(cleanupCount?: number): Promise<void>;\n}\n\nclass SortedSetRedisSessionExpirationStore implements RedisSessionExpirationStore {\n  private readonly redis: Redis;\n  private readonly namespace: Namespace;\n  private readonly expirationsKey: string;\n\n  constructor(redis: Redis, namespace: Namespace) {\n    this.redis = redis;\n    this.namespace = namespace;\n    this.expirationsKey = `${this.namespace}:sessions:expirations`;\n  }\n\n  getSessionKey(sessionId: string) {\n    return `${this.namespace}:sessions:${sessionId}`;\n  }\n\n  async save(session: RedisSession) {\n    const expirationInMillis =\n      session.getLastAccessedTime() + session.getMaxInactiveInterval() * 1000;\n    await this.redis.zadd(this.expirationsKey, expirationInMillis, session.getId());\n  }\n\n  async remove(sessionId: string) {\n    await this.redis.zrem(this.expirationsKey, sessionId);\n  }\n\n  async rename(session: RedisSession) {\n    if (session.originalSessionId === null || session.originalSessionId === session.getId()) return;\n    await this.remove(session.originalSessionId);\n    await this.save(session);\n  }\n\n  async cleanupExpiredSessions(cleanupCount = 100) {\n    const key = this.expirationsKey;\n    const score = Date.now();\n    const sessionIds = await this.redis.zrevrangebyscore(key, score, 0, 'LIMIT', 0, cleanupCount);\n    if (sessionIds.length === 0) return;\n    for (const sessionId of sessionIds) {\n      const sessionKey = this.getSessionKey(sessionId);\n      // Checks if the session exists. By trying to access the session we only trigger a deletion\n      // if the TTL is expired.\n      await this.redis.exists(sessionKey);\n    }\n  }\n}\n\n// <spring:session:sessions:uuid, hash<session>> + 300s\n// <spring:session:sessions:expirations, sorted_set<uuid>>\n// <spring:session:sessions:expires:uuid, string<empty>>: for expire check & event\nexport class RedisIndexedSessionRepository implements SessionRepository<RedisSession> {\n  private readonly redis: Redis;\n  private readonly namespace: Namespace;\n  private readonly expirationStore: RedisSessionExpirationStore;\n\n  private defaultMaxInactiveInterval = 1800;\n\n  constructor(redis: Redis, namespace: Namespace = 'spring:session') {\n    this.redis = redis;\n    this.namespace = namespace;\n    this.expirationStore = new SortedSetRedisSessionExpirationStore(redis, namespace);\n  }\n\n  setDefaultMaxInactiveInterval(interval: number) {\n    this.defaultMaxInactiveInterval = interval;\n  }\n\n  private getSessionKey(sessionId: string) {\n    return `${this.namespace}:sessions:${sessionId}`;\n  }\n\n  private getExpiredKey(sessionId: string) {\n    return `${this.namespace}:sessions:expires:${sessionId}`;\n  }\n\n  private getSessionAttrNameKey(name: string) {\n    return ATTRIBUTE_PREFIX + name;\n  }\n\n  private getPrincipalKey(principalName: string) {\n    return `${this.namespace}:index:${PRINCIPAL_NAME_INDEX_NAME}:${principalName}`;\n  }\n\n  private async getSession(id: string, allowExpired: boolean): Promise<RedisSession | null> {\n    const key = this.getSessionKey(id);\n    const entries = await this.redis.hgetall(key);\n    if (!entries || Object.keys(entries).length === 0) return null;\n    const loaded = new MapSession(id);\n    if (!entries.creationTime) throw new Error('creationTime attribute not found');\n    if (!entries.lastAccessedTime) throw new Error('lastAccessedTime attribute not found');\n    if (!entries.maxInactiveInterval) throw new Error('maxInactiveInterval attribute not found');\n\n    loaded.setCreationTime(Number(entries.creationTime));\n    loaded.setLastAccessedTime(Number(entries.lastAccessedTime));\n    loaded.setMaxInactiveInterval(Number(entries.maxInactiveInterval));\n\n    for (const [key, value] of Object.entries(entries)) {\n      if (key.startsWith(ATTRIBUTE_PREFIX)) {\n        loaded.setAttribute(key.slice(ATTRIBUTE_PREFIX.length), value);\n      }\n    }\n\n    if (!allowExpired && loaded.isExpired()) return null;\n    const result = new RedisSession(loaded, false);\n    result.originalLastAccessTime = loaded.getLastAccessedTime();\n    return result;\n  }\n\n  private async saveDelta(session: RedisSession) {\n    if (session.delta.size === 0) return;\n    const sessionId = session.getId();\n    const delta = new Map<string, string | number>();\n    const removes: string[] = [];\n    session.delta.forEach((value, key) => {\n      if (value !== null) delta.set(key, value);\n      else removes.push(key);\n    });\n\n    if (delta.size !== 0) {\n      await this.redis.hset(this.getSessionKey(sessionId), delta);\n    }\n    if (removes.length !== 0) {\n      await this.redis.hdel(this.getSessionKey(sessionId), ...removes);\n    }\n\n    const principalSessionKey = this.getSessionAttrNameKey(PRINCIPAL_NAME_INDEX_NAME);\n    const securityPrincipalSessionKey = this.getSessionAttrNameKey(SPRING_SECURITY_CONTEXT);\n    if (session.delta.has(principalSessionKey) || session.delta.has(securityPrincipalSessionKey)) {\n      if (session.originalPrincipalName !== null) {\n        const originalPrincipalRedisKey = this.getPrincipalKey(session.originalPrincipalName);\n        await this.redis.srem(originalPrincipalRedisKey, sessionId);\n      }\n      const principal = resolveIndexesFor(session);\n      session.originalPrincipalName = principal;\n      if (principal !== null) {\n        const principalRedisKey = this.getPrincipalKey(principal);\n        await this.redis.sadd(principalRedisKey, sessionId);\n      }\n    }\n\n    if (session.isNew) {\n      // here your can send session created message to channel\n      session.isNew = false;\n    }\n\n    const sessionExpireInSeconds = session.getMaxInactiveInterval();\n\n    // createShadowKey start\n    const keyToExpire = SESSION_EXPIRES_PREFIX + session.getId();\n    const sessionKey = this.getSessionKey(keyToExpire);\n\n    if (sessionExpireInSeconds < 0) {\n      await this.redis.append(sessionKey, '');\n      await this.redis.persist(sessionKey);\n      await this.redis.persist(this.getSessionKey(session.getId()));\n    } else if (sessionExpireInSeconds === 0) {\n      await this.redis.del(sessionKey);\n    } else {\n      await this.redis.append(sessionKey, '');\n      await this.redis.expire(sessionKey, sessionExpireInSeconds);\n    }\n    // createShadowKey end\n\n    if (sessionExpireInSeconds > 0) {\n      const fiveMinutesAfterExpires = sessionExpireInSeconds + 5 * 60;\n      await this.redis.expire(this.getSessionKey(session.getId()), fiveMinutesAfterExpires);\n    }\n\n    await this.expirationStore.save(session);\n    session.delta.clear();\n  }\n\n  private async saveChangeSessionId(session: RedisSession) {\n    const sessionId = session.getId();\n    if (sessionId === session.originalSessionId) return;\n    if (!session.isNew) {\n      const originalSessionIdKey = this.getSessionKey(session.originalSessionId);\n      const sessionIdKey = this.getSessionKey(sessionId);\n      const originalExpiredKey = this.getExpiredKey(session.originalSessionId);\n      const expiredKey = this.getExpiredKey(sessionId);\n\n      await this.redis.rename(originalSessionIdKey, sessionIdKey);\n      await this.redis.rename(originalExpiredKey, expiredKey);\n\n      if (session.originalPrincipalName !== null) {\n        const originalPrincipalRedisKey = this.getPrincipalKey(session.originalPrincipalName);\n        await this.redis.srem(originalPrincipalRedisKey, session.originalSessionId);\n        await this.redis.sadd(originalPrincipalRedisKey, sessionId);\n      }\n      await this.expirationStore.rename(session);\n    }\n    session.originalSessionId = sessionId;\n  }\n\n  // override\n  createSession() {\n    const cached = new MapSession();\n    cached.setMaxInactiveInterval(this.defaultMaxInactiveInterval);\n    return new RedisSession(cached, true);\n  }\n\n  async save(session: RedisSession) {\n    await this.saveChangeSessionId(session);\n    await this.saveDelta(session);\n  }\n\n  async findById(sessionId: string): Promise<RedisSession | null> {\n    return this.getSession(sessionId, false);\n  }\n\n  async deleteById(sessionId: string) {\n    const session = await this.getSession(sessionId, true);\n    if (!session) return;\n    // 1. cleanup index\n    const principal = resolveIndexesFor(session);\n    if (principal !== null) {\n      await this.redis.srem(this.getPrincipalKey(principal), sessionId);\n    }\n    // 2. delete in set\n    await this.expirationStore.remove(sessionId);\n    // 3. delete expired key\n    const expiredKey = this.getExpiredKey(sessionId);\n    await this.redis.del(expiredKey);\n\n    session.setMaxInactiveInterval(0);\n    await this.save(session);\n  }\n\n  async findByIndexNameAndIndexValue(indexName: string, indexValue: string) {\n    if (indexName !== PRINCIPAL_NAME_INDEX_NAME) {\n      return new Map<string, RedisSession>();\n    }\n\n    const principalKey = this.getPrincipalKey(indexValue);\n    const sessionIds = await this.redis.smembers(principalKey);\n    if (!sessionIds || sessionIds.length === 0) {\n      return new Map<string, RedisSession>();\n    }\n    const sessions = new Map<string, RedisSession>();\n    for (const sessionId of sessionIds) {\n      const session = await this.findById(sessionId);\n      if (session !== null) {\n        sessions.set(sessionId, session);\n      }\n    }\n    return sessions;\n  }\n\n  async findByPrincipalName(principalName: string) {\n    return this.findByIndexNameAndIndexValue(PRINCIPAL_NAME_INDEX_NAME, principalName);\n  }\n\n  async cleanupExpiredSessions(cleanupCount?: number) {\n    await this.expirationStore.cleanupExpiredSessions(cleanupCount);\n  }\n}\n\nexport class RedisKVRepository implements KVRepository {\n  private readonly redis: Redis;\n\n  constructor(redis: Redis) {\n    this.redis = redis;\n  }\n\n  async setItem(key: string, value: string, expiresIn?: number) {\n    if (expiresIn) {\n      await this.redis.set(key, value, 'EX', expiresIn);\n    } else {\n      await this.redis.set(key, value);\n    }\n  }\n\n  async getItem(key: string): Promise<string | null> {\n    return await this.redis.get(key);\n  }\n\n  async removeItem(key: string) {\n    await this.redis.del(key);\n  }\n}\n"],"mappings":"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","names":["key"]}