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

teableio / teable / 8389034568

22 Mar 2024 10:38AM UTC coverage: 79.934% (+51.7%) from 28.208%
8389034568

Pull #487

github

web-flow
Merge 3045b1f94 into a06c6afb1
Pull Request #487: refactor: move zod schema to openapi

3263 of 3860 branches covered (84.53%)

67 of 70 new or added lines in 23 files covered. (95.71%)

762 existing lines in 27 files now uncovered.

25152 of 31466 relevant lines covered (79.93%)

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

192✔
40
  private async getCache(sid: string) {
192✔
41
    const expire = await this.cacheService.get(`auth:session-expire:${sid}`);
12,470✔
42
    if (expire) {
12,470!
UNCOV
43
      return null;
×
UNCOV
44
    }
×
45
    const session = await this.cacheService.get(`auth:session-store:${sid}`);
12,470✔
46
    if (!session) {
12,470!
UNCOV
47
      return null;
×
UNCOV
48
    }
×
49
    const userId = session.passport.user.id;
12,470✔
50
    const userSessions = (await this.cacheService.get(`auth:session-user:${userId}`)) ?? {};
12,470!
51
    if (!userSessions[sid]) {
12,470!
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,470✔
56
    // so that the user session does not expire while the session is still alive.
12,470✔
57
    const nowSec = Math.floor(Date.now() / 1000);
12,470✔
58
    if (userSessions[sid] < nowSec) {
12,470!
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,470✔
65
  }
12,470✔
66

192✔
67
  async get(
192✔
68
    sid: string,
6,312✔
69
    callback: (err: unknown, session?: ISessionData | null | undefined) => void
6,312✔
70
  ): Promise<void> {
6,312✔
71
    try {
6,312✔
72
      const session = await this.getCache(sid);
6,312✔
73
      callback(null, session);
6,312✔
74
    } catch (error) {
6,312!
UNCOV
75
      callback(error);
×
UNCOV
76
    }
×
77
  }
6,312✔
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,158✔
99
    session: ISessionData,
6,158✔
100
    callback?: ((err?: unknown) => void) | undefined
6,158✔
101
  ) {
6,158✔
102
    try {
6,158✔
103
      const sessionCache = await this.getCache(sid);
6,158✔
104
      if (sessionCache) {
6,158✔
105
        await this.setCache(sid, session);
6,158✔
106
        callback?.();
6,158✔
107
        return;
6,158✔
108
      }
6,158!
UNCOV
109
      callback?.(new Error('Session not found'));
×
110
    } catch (error) {
6,158!
UNCOV
111
      callback?.(error);
×
UNCOV
112
    }
×
113
  }
6,158✔
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