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

u-wave / core / 17172987484

23 Aug 2025 07:45AM UTC coverage: 85.077%. Remained the same
17172987484

push

github

web-flow
Fix pino logging calls, make types pass (#707)

929 of 1106 branches covered (84.0%)

Branch coverage included in aggregate %.

0 of 2 new or added lines in 2 files covered. (0.0%)

9983 of 11720 relevant lines covered (85.18%)

90.48 hits per line

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

88.82
/src/controllers/now.js
1
import { getBoothData } from './booth.js';
1✔
2
import { serializeCurrentUser, serializePlaylist, serializeUser } from '../utils/serialize.js';
1✔
3
import { legacyPlaylistItem } from './playlists.js';
1✔
4
import { REDIS_ACTIVE_SESSIONS } from '../SocketServer.js';
1✔
5

1✔
6
/**
1✔
7
 * @typedef {import('../schema.js').UserID} UserID
1✔
8
 */
1✔
9

1✔
10
/**
1✔
11
 * @param {import('../Uwave.js').default} uw
1✔
12
 * @param {import('../schema.js').Playlist & { size: number }} playlist
1✔
13
 */
1✔
14
async function getFirstItem(uw, playlist) {
1✔
15
  try {
1✔
16
    if (playlist.size > 0) {
1!
17
      const { playlistItem, media } = await uw.playlists.getPlaylistItemAt(playlist, 0);
×
18
      return legacyPlaylistItem(playlistItem, media);
×
19
    }
×
20
  } catch {
1!
21
    // Nothing
×
22
  }
×
23
  return null;
1✔
24
}
1✔
25

1✔
26
/**
1✔
27
 * @param {unknown} str
1✔
28
 */
1✔
29
function toInt(str) {
5✔
30
  if (typeof str !== 'string') return 0;
5✔
31
  if (!/^\d+$/.test(str)) return 0;
×
32
  return parseInt(str, 10);
×
33
}
5✔
34

1✔
35
/**
1✔
36
 * @param {import('../Uwave.js').default} uw
1✔
37
 */
1✔
38
async function getOnlineUsers(uw) {
5✔
39
  const userIDs = /** @type {UserID[]} */ (await uw.redis.lrange(REDIS_ACTIVE_SESSIONS, 0, -1));
5✔
40
  if (userIDs.length === 0) {
5✔
41
    return [];
4✔
42
  }
4✔
43

1✔
44
  const users = await uw.users.getUsersByIds(userIDs);
1✔
45
  return users.map(serializeUser);
1✔
46
}
5✔
47

1✔
48
/**
1✔
49
 * @param {import('../Uwave.js').default} uw
1✔
50
 */
1✔
51
async function getGuestsCount(uw) {
5✔
52
  const guests = await uw.redis.get('http-api:guests');
5✔
53
  return toInt(guests);
5✔
54
}
5✔
55

1✔
56
/**
1✔
57
 * @type {import('../types.js').Controller}
1✔
58
 */
1✔
59
async function getState(req) {
5✔
60
  const uw = req.uwave;
5✔
61
  const { authRegistry } = req.uwaveHttp;
5✔
62
  const { passport } = uw;
5✔
63
  const { user, sessionID } = req;
5✔
64

5✔
65
  const motd = uw.motd.get();
5✔
66
  const users = getOnlineUsers(uw);
5✔
67
  const guests = getGuestsCount(uw);
5✔
68
  const roles = uw.acl.getAllRoles();
5✔
69
  const booth = getBoothData(uw);
5✔
70
  const waitlist = uw.waitlist.getUserIDs();
5✔
71
  const waitlistLocked = uw.waitlist.isLocked();
5✔
72
  const autoLeave = user != null ? uw.booth.getRemoveAfterCurrentPlay(user) : false;
5✔
73
  let activePlaylist = user?.activePlaylistID
5✔
74
    ? uw.playlists.getUserPlaylist(user, user.activePlaylistID).catch((error) => {
5✔
75
      // If the playlist was not found, our database is inconsistent. A deleted or nonexistent
×
76
      // playlist should never be listed as the active playlist. Most likely this is not the
×
77
      // user's fault, so we should not error out on `/api/now`. Instead, pretend they don't have
×
78
      // an active playlist at all. Clients can then let them select a new playlist to activate.
×
79
      if (error.code === 'NOT_FOUND' || error.code === 'playlist-not-found') {
×
NEW
80
        req.log.warn({ err: error }, 'The active playlist does not exist');
×
81
        return null;
×
82
      }
×
83
      throw error;
×
84
    })
1✔
85
    : Promise.resolve(null);
5✔
86
  const playlists = user ? uw.playlists.getUserPlaylists(user) : null;
5✔
87
  const firstActivePlaylistItem = activePlaylist.then((playlist) => (
5✔
88
    playlist != null ? getFirstItem(uw, playlist) : null
5✔
89
  ));
5✔
90
  const socketToken = user ? authRegistry.createAuthToken(user, sessionID) : null;
5✔
91
  const authStrategies = passport.strategies();
5✔
92
  const time = Date.now();
5✔
93

5✔
94
  const stateShape = {
5✔
95
    motd,
5✔
96
    user: user ? serializeCurrentUser(user) : null,
5✔
97
    users,
5✔
98
    guests,
5✔
99
    roles,
5✔
100
    booth,
5✔
101
    waitlist,
5✔
102
    waitlistLocked,
5✔
103
    autoLeave,
5✔
104
    activePlaylist: activePlaylist.then((playlist) => playlist?.id ?? null),
5✔
105
    firstActivePlaylistItem,
5✔
106
    playlists,
5✔
107
    socketToken,
5✔
108
    authStrategies,
5✔
109
    time,
5✔
110
  };
5✔
111

5✔
112
  const stateKeys = Object.keys(stateShape);
5✔
113
  // This is a little dirty but maintaining the exact type shape is very hard here.
5✔
114
  // We could solve that in the future by using a `p-props` style function. The npm
5✔
115
  // module `p-props` is a bit wasteful though.
5✔
116
  /** @type {any} */
5✔
117
  const values = Object.values(stateShape);
5✔
118
  const stateValues = await Promise.all(values);
5✔
119

5✔
120
  const state = Object.create(null);
5✔
121
  for (let i = 0; i < stateKeys.length; i += 1) {
5✔
122
    state[stateKeys[i]] = stateValues[i];
75✔
123
  }
75✔
124

5✔
125
  if (state.playlists) {
5✔
126
    state.playlists = state.playlists.map(serializePlaylist);
2✔
127
  }
2✔
128

5✔
129
  for (const permission of Object.values(state.roles).flat()) {
5✔
130
    // Web client expects all permissions to be roles too.
281✔
131
    // This isn't how it works since #637.
281✔
132
    // Clients can still distinguish between roles and permissions using `.includes('.')`
281✔
133
    state.roles[permission] ??= [];
281✔
134
  }
281✔
135

5✔
136
  return state;
5✔
137
}
5✔
138

1✔
139
export { getState };
1✔
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

© 2026 Coveralls, Inc