• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

teableio / teable / 8421671885

25 Mar 2024 02:23PM UTC coverage: 79.959% (+53.9%) from 26.087%
8421671885

Pull #496

github

web-flow
Merge f587f00fb into 9313e45fb
Pull Request #496: fix: unexpected link convert

3265 of 3865 branches covered (84.48%)

63 of 63 new or added lines in 4 files covered. (100.0%)

762 existing lines in 27 files now uncovered.

25183 of 31495 relevant lines covered (79.96%)

1188.33 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

75.0
/apps/nestjs-backend/src/features/auth/session/session-store.service.ts
1
/* eslint-disable @typescript-eslint/naming-convention */
2✔
2
import { Injectable } from '@nestjs/common';
2✔
3
import { Store } from 'express-session';
2✔
4
import { CacheService } from '../../../cache/cache.service';
2✔
5
import { AuthConfig, IAuthConfig } from '../../../configs/auth.config';
2✔
6
import type { ISessionData } from '../../../types/session';
2✔
7
import { second } from '../../../utils/second';
2✔
8

2✔
9
@Injectable()
2✔
10
export class SessionStoreService extends Store {
2✔
11
  private readonly ttl: number;
192✔
12
  private readonly userSessionExpire: number;
192✔
13

192✔
14
  constructor(
192✔
15
    private readonly cacheService: CacheService,
192✔
16
    @AuthConfig() private readonly authConfig: IAuthConfig
192✔
17
  ) {
192✔
18
    super();
192✔
19
    this.ttl = second(this.authConfig.session.expiresIn);
192✔
20
    this.userSessionExpire = this.ttl + 60 * 2;
192✔
21
  }
192✔
22

192✔
23
  private async setCache(sid: string, session: ISessionData) {
192✔
24
    const userId = session.passport.user.id;
6,340✔
25
    const userSessions = (await this.cacheService.get(`auth:session-user:${userId}`)) ?? {};
6,340✔
26
    // The expiration time is greater than the session cache time,
6,340✔
27
    // so that the user session does not expire while the session is still alive.
6,340✔
28
    const nowSec = Math.floor(Date.now() / 1000);
6,340✔
29
    userSessions[sid] = nowSec + this.userSessionExpire;
6,340✔
30
    // Maintain userSession, remove expired keys
6,340✔
31
    for (const [key, value] of Object.entries(userSessions)) {
6,340✔
32
      if (value < nowSec) {
6,400!
UNCOV
33
        delete userSessions[key];
×
UNCOV
34
      }
×
35
    }
6,400✔
36
    await this.cacheService.set(`auth:session-user:${userId}`, userSessions, this.ttl);
6,340✔
37
    await this.cacheService.set(`auth:session-store:${sid}`, session, this.ttl);
6,340✔
38
  }
6,340✔
39

192✔
40
  private async getCache(sid: string) {
192✔
41
    const expire = await this.cacheService.get(`auth:session-expire:${sid}`);
12,522✔
42
    if (expire) {
12,522!
UNCOV
43
      return null;
×
UNCOV
44
    }
×
45
    const session = await this.cacheService.get(`auth:session-store:${sid}`);
12,522✔
46
    if (!session) {
12,522!
UNCOV
47
      return null;
×
UNCOV
48
    }
×
49
    const userId = session.passport.user.id;
12,522✔
50
    const userSessions = (await this.cacheService.get(`auth:session-user:${userId}`)) ?? {};
12,522!
51
    if (!userSessions[sid]) {
12,522!
UNCOV
52
      await this.cacheService.del(`auth:session-store:${sid}`);
×
UNCOV
53
      return null;
×
UNCOV
54
    }
×
55
    // The expiration time is greater than the session cache time,
12,522✔
56
    // so that the user session does not expire while the session is still alive.
12,522✔
57
    const nowSec = Math.floor(Date.now() / 1000);
12,522✔
58
    if (userSessions[sid] < nowSec) {
12,522!
UNCOV
59
      delete userSessions[sid];
×
UNCOV
60
      await this.cacheService.del(`auth:session-store:${sid}`);
×
UNCOV
61
      await this.cacheService.set(`auth:session-user:${userId}`, userSessions, this.ttl);
×
UNCOV
62
      return null;
×
UNCOV
63
    }
×
64
    return session;
12,522✔
65
  }
12,522✔
66

192✔
67
  async get(
192✔
68
    sid: string,
6,338✔
69
    callback: (err: unknown, session?: ISessionData | null | undefined) => void
6,338✔
70
  ): Promise<void> {
6,338✔
71
    try {
6,338✔
72
      const session = await this.getCache(sid);
6,338✔
73
      callback(null, session);
6,338✔
74
    } catch (error) {
6,338!
UNCOV
75
      callback(error);
×
UNCOV
76
    }
×
77
  }
6,338✔
78

192✔
79
  async set(sid: string, session: ISessionData, callback?: ((err?: unknown) => void) | undefined) {
192✔
80
    try {
156✔
81
      await this.setCache(sid, session);
156✔
82
      callback?.();
156✔
83
    } catch (error) {
156!
UNCOV
84
      callback?.(error);
×
UNCOV
85
    }
×
86
  }
156✔
87

192✔
88
  async destroy(sid: string, callback?: ((err?: unknown) => void) | undefined) {
192✔
89
    try {
78✔
90
      await this.cacheService.del(`auth:session-store:${sid}`);
78✔
91
      callback?.();
78✔
92
    } catch (error) {
78!
UNCOV
93
      callback?.(error);
×
UNCOV
94
    }
×
95
  }
78✔
96

192✔
97
  async touch(
192✔
98
    sid: string,
6,184✔
99
    session: ISessionData,
6,184✔
100
    callback?: ((err?: unknown) => void) | undefined
6,184✔
101
  ) {
6,184✔
102
    try {
6,184✔
103
      const sessionCache = await this.getCache(sid);
6,184✔
104
      if (sessionCache) {
6,184✔
105
        await this.setCache(sid, session);
6,184✔
106
        callback?.();
6,184✔
107
        return;
6,184✔
108
      }
6,184!
UNCOV
109
      callback?.(new Error('Session not found'));
×
110
    } catch (error) {
6,184!
UNCOV
111
      callback?.(error);
×
UNCOV
112
    }
×
113
  }
6,184✔
114

192✔
115
  async clearByUserId(userId: string) {
192✔
UNCOV
116
    const userSessions = (await this.cacheService.get(`auth:session-user:${userId}`)) ?? {};
×
UNCOV
117
    for (const sid of Object.keys(userSessions)) {
×
UNCOV
118
      // Preventing competition
×
UNCOV
119
      await this.cacheService.set(`auth:session-expire:${sid}`, true, 60);
×
UNCOV
120
      await this.cacheService.del(`auth:session-store:${sid}`);
×
UNCOV
121
    }
×
UNCOV
122
    await this.cacheService.del(`auth:session-user:${userId}`);
×
UNCOV
123
  }
×
124
}
192✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc