@oxy.so/core 7.12.0 → 7.13.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/api/apps.js +1 -1
- package/dist/cjs/api/auth.js +1 -1
- package/dist/cjs/api/billing.js +4 -3
- package/dist/cjs/api/devices.js +5 -4
- package/dist/cjs/api/follows.js +12 -10
- package/dist/cjs/api/privacy.js +8 -7
- package/dist/cjs/api/session.js +10 -7
- package/dist/cjs/api/topics.js +4 -3
- package/dist/cjs/api/users.js +5 -4
- package/dist/cjs/i18n/locales/compact/es-ES.generated.js +1 -1
- package/dist/cjs/i18n/locales/en-US.js +38 -15
- package/dist/cjs/i18n/locales/es-ES.js +38 -15
- package/dist/cjs/utils/identityCacheSweep.js +9 -1
- package/dist/esm/api/apps.js +1 -1
- package/dist/esm/api/auth.js +1 -1
- package/dist/esm/api/billing.js +4 -3
- package/dist/esm/api/devices.js +5 -4
- package/dist/esm/api/follows.js +12 -10
- package/dist/esm/api/privacy.js +8 -7
- package/dist/esm/api/session.js +10 -7
- package/dist/esm/api/topics.js +4 -3
- package/dist/esm/api/users.js +6 -5
- package/dist/esm/i18n/locales/compact/es-ES.generated.js +1 -1
- package/dist/esm/i18n/locales/en-US.js +38 -15
- package/dist/esm/i18n/locales/es-ES.js +38 -15
- package/dist/esm/utils/identityCacheSweep.js +8 -1
- package/dist/types/i18n/locales/en-US.d.ts +35 -12
- package/dist/types/i18n/locales/es-ES.d.ts +35 -12
- package/dist/types/utils/identityCacheSweep.d.ts +5 -0
- package/package.json +2 -2
- package/src/api/apps.ts +1 -1
- package/src/api/auth.ts +9 -4
- package/src/api/billing.ts +12 -5
- package/src/api/devices.ts +6 -4
- package/src/api/follows.ts +21 -10
- package/src/api/privacy.ts +9 -7
- package/src/api/session.ts +18 -9
- package/src/api/topics.ts +5 -3
- package/src/api/users.ts +11 -5
- package/src/i18n/locales/compact/es-ES.generated.ts +1 -1
- package/src/i18n/locales/en-US.ts +39 -15
- package/src/i18n/locales/es-ES.ts +40 -15
- package/src/utils/identityCacheSweep.ts +9 -1
package/src/api/devices.ts
CHANGED
|
@@ -40,6 +40,8 @@ import type {
|
|
|
40
40
|
import { logger } from '../logger';
|
|
41
41
|
import { extractErrorStatus } from '../utils/errorUtils';
|
|
42
42
|
|
|
43
|
+
const enc = encodeURIComponent;
|
|
44
|
+
|
|
43
45
|
/** One of the signed-in user's devices (`GET /devices`). */
|
|
44
46
|
export interface UserDevice {
|
|
45
47
|
id: string;
|
|
@@ -139,7 +141,7 @@ export class DevicesApi {
|
|
|
139
141
|
* @param deviceId - The device ID to remove
|
|
140
142
|
*/
|
|
141
143
|
async remove(deviceId: string): Promise<void> {
|
|
142
|
-
await this.ctx.request('DELETE', `/devices/${deviceId}`, undefined, { cache: false });
|
|
144
|
+
await this.ctx.request('DELETE', `/devices/${enc(deviceId)}`, undefined, { cache: false });
|
|
143
145
|
}
|
|
144
146
|
|
|
145
147
|
/**
|
|
@@ -152,7 +154,7 @@ export class DevicesApi {
|
|
|
152
154
|
// Cache disabled by default to ensure fresh session data
|
|
153
155
|
return this.ctx.request<DeviceLinkedSession[]>(
|
|
154
156
|
'GET',
|
|
155
|
-
`/session/device/sessions/${sessionId}`,
|
|
157
|
+
`/session/device/sessions/${enc(sessionId)}`,
|
|
156
158
|
undefined,
|
|
157
159
|
{
|
|
158
160
|
cache: false, // Don't cache sessions - always get fresh data
|
|
@@ -178,7 +180,7 @@ export class DevicesApi {
|
|
|
178
180
|
if (excludeCurrent) urlParams.excludeCurrent = 'true';
|
|
179
181
|
return this.ctx.request<DeviceLinkedSessionLogoutResponse>(
|
|
180
182
|
'POST',
|
|
181
|
-
`/session/device/logout-all/${sessionId}`,
|
|
183
|
+
`/session/device/logout-all/${enc(sessionId)}`,
|
|
182
184
|
urlParams,
|
|
183
185
|
{ cache: false },
|
|
184
186
|
);
|
|
@@ -196,7 +198,7 @@ export class DevicesApi {
|
|
|
196
198
|
): Promise<{ success: boolean; message: string; deviceName: string }> {
|
|
197
199
|
return this.ctx.request(
|
|
198
200
|
'PUT',
|
|
199
|
-
`/session/device/name/${sessionId}`,
|
|
201
|
+
`/session/device/name/${enc(sessionId)}`,
|
|
200
202
|
{ deviceName },
|
|
201
203
|
{ cache: false },
|
|
202
204
|
);
|
package/src/api/follows.ts
CHANGED
|
@@ -27,6 +27,7 @@ import {
|
|
|
27
27
|
buildUrl,
|
|
28
28
|
type FollowGraphParams,
|
|
29
29
|
} from '../utils/apiUtils';
|
|
30
|
+
import { oxyUserByIdCacheKey, oxyUserByIdPath } from '../utils/identityCacheSweep';
|
|
30
31
|
|
|
31
32
|
const GRAPH_TTL = 2 * 60 * 1000;
|
|
32
33
|
const STATUS_TTL = 60 * 1000;
|
|
@@ -130,7 +131,7 @@ export class FollowsApi {
|
|
|
130
131
|
async follow(userId: string): Promise<FollowMutationResult> {
|
|
131
132
|
const result = await this.ctx.request<FollowMutationResult>(
|
|
132
133
|
'POST',
|
|
133
|
-
|
|
134
|
+
`${oxyUserByIdPath(userId)}/follow`,
|
|
134
135
|
undefined,
|
|
135
136
|
{ cache: false },
|
|
136
137
|
);
|
|
@@ -155,7 +156,7 @@ export class FollowsApi {
|
|
|
155
156
|
async unfollow(userId: string): Promise<FollowMutationResult> {
|
|
156
157
|
const result = await this.ctx.request<FollowMutationResult>(
|
|
157
158
|
'DELETE',
|
|
158
|
-
|
|
159
|
+
`${oxyUserByIdPath(userId)}/follow`,
|
|
159
160
|
undefined,
|
|
160
161
|
{ cache: false },
|
|
161
162
|
);
|
|
@@ -178,7 +179,7 @@ export class FollowsApi {
|
|
|
178
179
|
|
|
179
180
|
/** Whether the signed-in user follows `userId`. Cached 1 minute, busted by every write. */
|
|
180
181
|
async status(userId: string): Promise<{ isFollowing: boolean }> {
|
|
181
|
-
return this.ctx.request('GET',
|
|
182
|
+
return this.ctx.request('GET', `${oxyUserByIdPath(userId)}/follow-status`, undefined, {
|
|
182
183
|
cache: true,
|
|
183
184
|
cacheTTL: STATUS_TTL,
|
|
184
185
|
});
|
|
@@ -221,13 +222,19 @@ export class FollowsApi {
|
|
|
221
222
|
* `limit`/`offset`/`sort` combination is its own cache entry (2 minutes).
|
|
222
223
|
*/
|
|
223
224
|
async followers(userId: string, params?: FollowGraphParams): Promise<UserPage<'followers'>> {
|
|
224
|
-
const { data, total, hasMore } = await this.userPage(
|
|
225
|
+
const { data, total, hasMore } = await this.userPage(
|
|
226
|
+
`${oxyUserByIdPath(userId)}/followers`,
|
|
227
|
+
params,
|
|
228
|
+
);
|
|
225
229
|
return { followers: data, total, hasMore };
|
|
226
230
|
}
|
|
227
231
|
|
|
228
232
|
/** Who a user follows. `sort` as in `followers`. */
|
|
229
233
|
async following(userId: string, params?: FollowGraphParams): Promise<UserPage<'following'>> {
|
|
230
|
-
const { data, total, hasMore } = await this.userPage(
|
|
234
|
+
const { data, total, hasMore } = await this.userPage(
|
|
235
|
+
`${oxyUserByIdPath(userId)}/following`,
|
|
236
|
+
params,
|
|
237
|
+
);
|
|
231
238
|
return { following: data, total, hasMore };
|
|
232
239
|
}
|
|
233
240
|
|
|
@@ -236,7 +243,10 @@ export class FollowsApi {
|
|
|
236
243
|
* `userId`.
|
|
237
244
|
*/
|
|
238
245
|
async mutuals(userId: string, params?: FollowGraphParams): Promise<UserPage<'mutuals'>> {
|
|
239
|
-
const { data, total, hasMore } = await this.userPage(
|
|
246
|
+
const { data, total, hasMore } = await this.userPage(
|
|
247
|
+
`${oxyUserByIdPath(userId)}/mutuals`,
|
|
248
|
+
params,
|
|
249
|
+
);
|
|
240
250
|
return { mutuals: data, total, hasMore };
|
|
241
251
|
}
|
|
242
252
|
|
|
@@ -443,15 +453,16 @@ export class FollowsApi {
|
|
|
443
453
|
const keys: string[] = ['GET:/users/me/graph'];
|
|
444
454
|
const prefixes: string[] = ['GET:/profiles/username/', 'GET:/profiles/resolve'];
|
|
445
455
|
for (const id of targetUserIds) {
|
|
446
|
-
|
|
456
|
+
const user = oxyUserByIdPath(id);
|
|
457
|
+
keys.push(`GET:${user}/follow-status`);
|
|
447
458
|
// Profiles embed the viewer-relative `relationship`.
|
|
448
|
-
keys.push(
|
|
459
|
+
keys.push(oxyUserByIdCacheKey(id));
|
|
449
460
|
// The target gained or lost a follower, and "followers you know" with it.
|
|
450
|
-
prefixes.push(`GET
|
|
461
|
+
prefixes.push(`GET:${user}/followers`, `GET:${user}/mutuals`);
|
|
451
462
|
}
|
|
452
463
|
// The viewer's own following list changed.
|
|
453
464
|
const viewerId = this.ctx.oxy.session.userId;
|
|
454
|
-
if (viewerId) prefixes.push(`GET
|
|
465
|
+
if (viewerId) prefixes.push(`GET:${oxyUserByIdPath(viewerId)}/following`);
|
|
455
466
|
this.ctx.http.invalidateCache({ keys, prefixes });
|
|
456
467
|
}
|
|
457
468
|
}
|
package/src/api/privacy.ts
CHANGED
|
@@ -5,6 +5,8 @@ import type { BlockedUser, PrivacySettings, RestrictedUser } from '../models/int
|
|
|
5
5
|
import type { OxyContext } from '../client/context';
|
|
6
6
|
import { evictOxyIdentityCache } from '../utils/identityCacheSweep';
|
|
7
7
|
|
|
8
|
+
const enc = encodeURIComponent;
|
|
9
|
+
|
|
8
10
|
const LIST_TTL = 60 * 1000;
|
|
9
11
|
const SETTINGS_TTL = 2 * 60 * 1000;
|
|
10
12
|
|
|
@@ -20,7 +22,7 @@ export class PrivacyApi {
|
|
|
20
22
|
/** A user's privacy settings (default: the signed-in user's). */
|
|
21
23
|
async settings(userId?: string): Promise<PrivacySettings> {
|
|
22
24
|
const id = await this.resolveUserId(userId);
|
|
23
|
-
return this.ctx.request<PrivacySettings>('GET', `/privacy/${id}/privacy`, undefined, {
|
|
25
|
+
return this.ctx.request<PrivacySettings>('GET', `/privacy/${enc(id)}/privacy`, undefined, {
|
|
24
26
|
cache: true,
|
|
25
27
|
cacheTTL: SETTINGS_TTL,
|
|
26
28
|
});
|
|
@@ -34,13 +36,13 @@ export class PrivacyApi {
|
|
|
34
36
|
const id = await this.resolveUserId(userId);
|
|
35
37
|
const res = await this.ctx.request<PrivacySettings>(
|
|
36
38
|
'PATCH',
|
|
37
|
-
`/privacy/${id}/privacy`,
|
|
39
|
+
`/privacy/${enc(id)}/privacy`,
|
|
38
40
|
settings,
|
|
39
41
|
{ cache: false },
|
|
40
42
|
);
|
|
41
43
|
// Privacy settings ride the user DTO, so every identity read goes stale too.
|
|
42
44
|
evictOxyIdentityCache(this.ctx.http, id);
|
|
43
|
-
this.ctx.oxy.cache.delete(`GET:/privacy/${id}/privacy`);
|
|
45
|
+
this.ctx.oxy.cache.delete(`GET:/privacy/${enc(id)}/privacy`);
|
|
44
46
|
return res;
|
|
45
47
|
}
|
|
46
48
|
|
|
@@ -59,7 +61,7 @@ export class PrivacyApi {
|
|
|
59
61
|
requireId(userId);
|
|
60
62
|
const res = await this.ctx.request<{ message: string }>(
|
|
61
63
|
'POST',
|
|
62
|
-
`/privacy/blocked/${userId}`,
|
|
64
|
+
`/privacy/blocked/${enc(userId)}`,
|
|
63
65
|
undefined,
|
|
64
66
|
{ cache: false },
|
|
65
67
|
);
|
|
@@ -72,7 +74,7 @@ export class PrivacyApi {
|
|
|
72
74
|
requireId(userId);
|
|
73
75
|
const res = await this.ctx.request<{ message: string }>(
|
|
74
76
|
'DELETE',
|
|
75
|
-
`/privacy/blocked/${userId}`,
|
|
77
|
+
`/privacy/blocked/${enc(userId)}`,
|
|
76
78
|
undefined,
|
|
77
79
|
{ cache: false },
|
|
78
80
|
);
|
|
@@ -105,7 +107,7 @@ export class PrivacyApi {
|
|
|
105
107
|
requireId(userId);
|
|
106
108
|
const res = await this.ctx.request<{ message: string }>(
|
|
107
109
|
'POST',
|
|
108
|
-
`/privacy/restricted/${userId}`,
|
|
110
|
+
`/privacy/restricted/${enc(userId)}`,
|
|
109
111
|
undefined,
|
|
110
112
|
{ cache: false },
|
|
111
113
|
);
|
|
@@ -118,7 +120,7 @@ export class PrivacyApi {
|
|
|
118
120
|
requireId(userId);
|
|
119
121
|
const res = await this.ctx.request<{ message: string }>(
|
|
120
122
|
'DELETE',
|
|
121
|
-
`/privacy/restricted/${userId}`,
|
|
123
|
+
`/privacy/restricted/${enc(userId)}`,
|
|
122
124
|
undefined,
|
|
123
125
|
{ cache: false },
|
|
124
126
|
);
|
package/src/api/session.ts
CHANGED
|
@@ -20,6 +20,8 @@ import type {
|
|
|
20
20
|
HolderEvidenceUnavailableReason,
|
|
21
21
|
} from './holderEvidence';
|
|
22
22
|
|
|
23
|
+
const enc = encodeURIComponent;
|
|
24
|
+
|
|
23
25
|
/** How long a refusal (disabled, rate limited, unreachable) is remembered per installation. */
|
|
24
26
|
const EVIDENCE_REFUSAL_MS = 5_000;
|
|
25
27
|
const unavailable = (reason: HolderEvidenceUnavailableReason): HolderEvidenceResult => ({
|
|
@@ -376,7 +378,7 @@ export class SessionApi {
|
|
|
376
378
|
)
|
|
377
379
|
: await this.ctx.request<SessionValidation>(
|
|
378
380
|
'GET',
|
|
379
|
-
`/session/validate/${sessionId}`,
|
|
381
|
+
`/session/validate/${enc(sessionId)}`,
|
|
380
382
|
{
|
|
381
383
|
...(options.deviceFingerprint
|
|
382
384
|
? { deviceFingerprint: options.deviceFingerprint }
|
|
@@ -407,7 +409,7 @@ export class SessionApi {
|
|
|
407
409
|
const invalid =
|
|
408
410
|
candidate?.status === 401 && candidate.code === 'OXY_LIVE_SESSION_INVALID_V1';
|
|
409
411
|
const denied = candidate?.status === 403 && candidate.code === 'OXY_LIVE_SESSION_POLICY_V1';
|
|
410
|
-
if (invalid) this.ctx.oxy.cache.delete(`GET:/session/user/${sessionId}`);
|
|
412
|
+
if (invalid) this.ctx.oxy.cache.delete(`GET:/session/user/${enc(sessionId)}`);
|
|
411
413
|
if (invalid || denied) throw error;
|
|
412
414
|
throw new OxyApiError('Live session authority is unavailable', {
|
|
413
415
|
status: 503,
|
|
@@ -416,29 +418,36 @@ export class SessionApi {
|
|
|
416
418
|
});
|
|
417
419
|
}
|
|
418
420
|
// The session is gone: drop any user cached for it (#196).
|
|
419
|
-
this.ctx.oxy.cache.delete(`GET:/session/user/${sessionId}`);
|
|
421
|
+
this.ctx.oxy.cache.delete(`GET:/session/user/${enc(sessionId)}`);
|
|
420
422
|
throw error;
|
|
421
423
|
}
|
|
422
424
|
}
|
|
423
425
|
|
|
424
426
|
/** The sessions that belong with `sessionId` (same account set). */
|
|
425
427
|
async list(sessionId: string): Promise<ClientSession[]> {
|
|
426
|
-
return this.ctx.request<ClientSession[]>(
|
|
427
|
-
|
|
428
|
-
|
|
428
|
+
return this.ctx.request<ClientSession[]>(
|
|
429
|
+
'GET',
|
|
430
|
+
`/session/sessions/${enc(sessionId)}`,
|
|
431
|
+
undefined,
|
|
432
|
+
{
|
|
433
|
+
cache: false,
|
|
434
|
+
},
|
|
435
|
+
);
|
|
429
436
|
}
|
|
430
437
|
|
|
431
438
|
/** Sign out `sessionId`, or — with `targetSessionId` — one of the sessions beside it. */
|
|
432
439
|
async logout(sessionId: string, targetSessionId?: string): Promise<void> {
|
|
433
440
|
const url = targetSessionId
|
|
434
|
-
? `/session/logout/${sessionId}/${targetSessionId}`
|
|
435
|
-
: `/session/logout/${sessionId}`;
|
|
441
|
+
? `/session/logout/${enc(sessionId)}/${enc(targetSessionId)}`
|
|
442
|
+
: `/session/logout/${enc(sessionId)}`;
|
|
436
443
|
await this.ctx.request('POST', url, undefined, { cache: false });
|
|
437
444
|
}
|
|
438
445
|
|
|
439
446
|
/** Sign out the other sessions for this account; preserve `sessionId`. */
|
|
440
447
|
async logoutAll(sessionId: string): Promise<void> {
|
|
441
|
-
await this.ctx.request('POST', `/session/logout-all/${sessionId}`, undefined, {
|
|
448
|
+
await this.ctx.request('POST', `/session/logout-all/${enc(sessionId)}`, undefined, {
|
|
449
|
+
cache: false,
|
|
450
|
+
});
|
|
442
451
|
}
|
|
443
452
|
|
|
444
453
|
/** Decoded claims of the current token, memoised per token string. */
|
package/src/api/topics.ts
CHANGED
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
import type { OxyContext } from '../client/context';
|
|
5
5
|
import type { TopicData, TopicListResult, TopicTranslation } from '../models/Topic';
|
|
6
6
|
|
|
7
|
+
const enc = encodeURIComponent;
|
|
8
|
+
|
|
7
9
|
const SHORT = 60 * 1000;
|
|
8
10
|
const LONG = 5 * 60 * 1000;
|
|
9
11
|
const EXTRA_LONG = 30 * 60 * 1000;
|
|
@@ -51,7 +53,7 @@ export class TopicsApi {
|
|
|
51
53
|
|
|
52
54
|
/** One topic by slug. */
|
|
53
55
|
async get(slug: string): Promise<TopicData> {
|
|
54
|
-
return this.ctx.request<TopicData>('GET', `/topics/${slug}`, undefined, {
|
|
56
|
+
return this.ctx.request<TopicData>('GET', `/topics/${enc(slug)}`, undefined, {
|
|
55
57
|
cache: true,
|
|
56
58
|
cacheTTL: LONG,
|
|
57
59
|
});
|
|
@@ -86,10 +88,10 @@ export class TopicsApi {
|
|
|
86
88
|
slug: string,
|
|
87
89
|
data: { description?: string; translations?: Record<string, TopicTranslation> },
|
|
88
90
|
): Promise<TopicData> {
|
|
89
|
-
const topic = await this.ctx.request<TopicData>('PATCH', `/topics/${slug}`, data, {
|
|
91
|
+
const topic = await this.ctx.request<TopicData>('PATCH', `/topics/${enc(slug)}`, data, {
|
|
90
92
|
cache: false,
|
|
91
93
|
});
|
|
92
|
-
this.ctx.oxy.cache.delete(`GET:/topics/${slug}`);
|
|
94
|
+
this.ctx.oxy.cache.delete(`GET:/topics/${enc(slug)}`);
|
|
93
95
|
return topic;
|
|
94
96
|
}
|
|
95
97
|
}
|
package/src/api/users.ts
CHANGED
|
@@ -12,7 +12,11 @@ import type { OxyContext } from '../client/context';
|
|
|
12
12
|
import type { PaginationInfo, SearchProfilesResponse, User } from '../models/interfaces';
|
|
13
13
|
import { buildQueryParams, type PaginationParams } from '../utils/apiUtils';
|
|
14
14
|
import { normalizeUserIdentity, normalizeUserIdentityOrNull } from '../utils/userIdentity';
|
|
15
|
-
import {
|
|
15
|
+
import {
|
|
16
|
+
OXY_IDENTITY_CACHE_PREFIXES,
|
|
17
|
+
oxyUserByIdCacheKey,
|
|
18
|
+
oxyUserByIdPath,
|
|
19
|
+
} from '../utils/identityCacheSweep';
|
|
16
20
|
import {
|
|
17
21
|
OXY_ACCOUNT_LIST_CACHE_KEY,
|
|
18
22
|
OXY_ACCOUNT_LIST_CACHE_QUERY_PREFIX,
|
|
@@ -22,6 +26,8 @@ import { extractErrorStatus } from '../utils/errorUtils';
|
|
|
22
26
|
import { type BatchLookup, runBatchLookup } from '../utils/batchLookup';
|
|
23
27
|
import { logger } from '../logger';
|
|
24
28
|
|
|
29
|
+
const enc = encodeURIComponent;
|
|
30
|
+
|
|
25
31
|
const PROFILE_TTL = 5 * 60 * 1000;
|
|
26
32
|
const SESSION_USER_TTL = 2 * 60 * 1000;
|
|
27
33
|
const ME_TTL = 60 * 1000;
|
|
@@ -65,7 +71,7 @@ export class UsersApi {
|
|
|
65
71
|
* registry-fresh read that neither serves nor overwrites a cached entry.
|
|
66
72
|
*/
|
|
67
73
|
async get(userId: string, options?: { cache?: boolean }): Promise<User> {
|
|
68
|
-
const user = await this.ctx.request<User>('GET',
|
|
74
|
+
const user = await this.ctx.request<User>('GET', oxyUserByIdPath(userId), undefined, {
|
|
69
75
|
cache: options?.cache ?? true,
|
|
70
76
|
cacheTTL: PROFILE_TTL,
|
|
71
77
|
});
|
|
@@ -166,7 +172,7 @@ export class UsersApi {
|
|
|
166
172
|
|
|
167
173
|
/** The user a session belongs to. */
|
|
168
174
|
async bySession(sessionId: string): Promise<User> {
|
|
169
|
-
const user = await this.ctx.request<User>('GET', `/session/user/${sessionId}`, undefined, {
|
|
175
|
+
const user = await this.ctx.request<User>('GET', `/session/user/${enc(sessionId)}`, undefined, {
|
|
170
176
|
cache: true,
|
|
171
177
|
cacheTTL: SESSION_USER_TTL,
|
|
172
178
|
});
|
|
@@ -271,7 +277,7 @@ export class UsersApi {
|
|
|
271
277
|
: (limitOrPagination ?? {});
|
|
272
278
|
const users = await this.ctx.request<User[]>(
|
|
273
279
|
'GET',
|
|
274
|
-
`/profiles/${userId}/similar`,
|
|
280
|
+
`/profiles/${enc(userId)}/similar`,
|
|
275
281
|
buildQueryParams(pagination),
|
|
276
282
|
{
|
|
277
283
|
cache: true,
|
|
@@ -289,7 +295,7 @@ export class UsersApi {
|
|
|
289
295
|
async moderators(communityId: string): Promise<User[]> {
|
|
290
296
|
const moderators = await this.ctx.request<User[]>(
|
|
291
297
|
'GET',
|
|
292
|
-
`/users/${communityId}/moderators`,
|
|
298
|
+
`/users/${enc(communityId)}/moderators`,
|
|
293
299
|
undefined,
|
|
294
300
|
{ cache: true, cacheTTL: PROFILE_TTL },
|
|
295
301
|
);
|