Merge upstream (Misskey.io)

This commit is contained in:
무라쿠모 2024-07-19 18:56:30 +09:00
commit 8bd86ce456
No known key found for this signature in database
GPG key ID: 139D6573F92DA9F7
42 changed files with 564 additions and 71 deletions

View file

@ -6,11 +6,10 @@
import { Inject, Injectable } from '@nestjs/common';
import ms from 'ms';
import { Endpoint } from '@/server/api/endpoint-base.js';
import { IdService } from '@/core/IdService.js';
import type { RenoteMutingsRepository } from '@/models/_.js';
import type { MiRenoteMuting } from '@/models/RenoteMuting.js';
import { DI } from '@/di-symbols.js';
import { GetterService } from '@/server/api/GetterService.js';
import { UserRenoteMutingService } from '@/core/UserRenoteMutingService.js';
import type { RenoteMutingsRepository } from '@/models/_.js';
import { ApiError } from '../../error.js';
export const meta = {
@ -64,7 +63,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
private renoteMutingsRepository: RenoteMutingsRepository,
private getterService: GetterService,
private idService: IdService,
private userRenoteMutingService: UserRenoteMutingService,
) {
super(meta, paramDef, async (ps, me) => {
const muter = me;
@ -81,21 +80,19 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
});
// Check if already muting
const exist = await this.renoteMutingsRepository.findOneBy({
muterId: muter.id,
muteeId: mutee.id,
const exist = await this.renoteMutingsRepository.exists({
where: {
muterId: muter.id,
muteeId: mutee.id,
},
});
if (exist != null) {
if (exist) {
throw new ApiError(meta.errors.alreadyMuting);
}
// Create mute
await this.renoteMutingsRepository.insert({
id: this.idService.gen(),
muterId: muter.id,
muteeId: mutee.id,
} as MiRenoteMuting);
await this.userRenoteMutingService.mute(muter, mutee);
});
}
}

View file

@ -11,6 +11,7 @@ import { QueryService } from '@/core/QueryService.js';
import { DI } from '@/di-symbols.js';
import { UserEntityService } from '@/core/entities/UserEntityService.js';
import { ApiError } from '../../error.js';
import type { Packed } from '@/misc/json-schema.js';
export const meta = {
tags: ['role', 'users'],
@ -92,10 +93,12 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
.limit(ps.limit)
.getMany();
return await Promise.all(assigns.map(async assign => ({
return (await Promise.allSettled(assigns.map(async assign => ({
id: assign.id,
user: await this.userEntityService.pack(assign.user!, me, { schema: 'UserDetailed' }),
})));
}))))
.filter((result): result is PromiseFulfilledResult<{ id: string; user: Packed<'UserDetailed'> }> => result.status === 'fulfilled')
.map(result => result.value);
});
}
}

View file

@ -6,6 +6,7 @@
import { Inject, Injectable } from '@nestjs/common';
import type { UsersRepository } from '@/models/_.js';
import { Endpoint } from '@/server/api/endpoint-base.js';
import PerUserPvChart from '@/core/chart/charts/per-user-pv.js';
import { QueryService } from '@/core/QueryService.js';
import { UserEntityService } from '@/core/entities/UserEntityService.js';
import { DI } from '@/di-symbols.js';
@ -31,7 +32,7 @@ export const paramDef = {
properties: {
limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 },
offset: { type: 'integer', default: 0 },
sort: { type: 'string', enum: ['+follower', '-follower', '+createdAt', '-createdAt', '+updatedAt', '-updatedAt'] },
sort: { type: 'string', enum: ['+follower', '-follower', '+createdAt', '-createdAt', '+updatedAt', '-updatedAt', '+pv', '-pv'] },
state: { type: 'string', enum: ['all', 'alive'], default: 'all' },
origin: { type: 'string', enum: ['combined', 'local', 'remote'], default: 'local' },
hostname: {
@ -49,6 +50,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
constructor(
@Inject(DI.usersRepository)
private usersRepository: UsersRepository,
private perUserPvChart: PerUserPvChart,
private userEntityService: UserEntityService,
private queryService: QueryService,
@ -70,7 +72,12 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
if (ps.hostname) {
query.andWhere('user.host = :hostname', { hostname: ps.hostname.toLowerCase() });
}
const chartUsers: { userId: string; count: number; }[] = [];
if (ps.sort?.endsWith('pv')) {
await this.perUserPvChart.getChartUsers('hour', 0, null, ps.limit, ps.offset).then(users => {
chartUsers.push(...users);
});
}
switch (ps.sort) {
case '+follower': query.orderBy('user.followersCount', 'DESC'); break;
case '-follower': query.orderBy('user.followersCount', 'ASC'); break;
@ -78,6 +85,16 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
case '-createdAt': query.orderBy('user.id', 'ASC'); break;
case '+updatedAt': query.andWhere('user.updatedAt IS NOT NULL').orderBy('user.updatedAt', 'DESC'); break;
case '-updatedAt': query.andWhere('user.updatedAt IS NOT NULL').orderBy('user.updatedAt', 'ASC'); break;
case '+pv':
if (chartUsers.length > 0) {
query.andWhere('user.id IN (:...userIds)', { userIds: chartUsers.map(user => user.userId) });
}
break;
case '-pv':
if (chartUsers.length > 0) {
query.andWhere('user.id IN (:...userIds)', { userIds: chartUsers.map(user => user.userId) });
}
break;
default: query.orderBy('user.id', 'ASC'); break;
}
@ -88,6 +105,19 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
query.offset(ps.offset);
const users = await query.getMany();
if (ps.sort === '+pv') {
users.sort((a, b) => {
const aPv = chartUsers.find(user => user.userId === a.id)?.count ?? 0;
const bPv = chartUsers.find(user => user.userId === b.id)?.count ?? 0;
return bPv - aPv;
});
} else if (ps.sort === '-pv') {
users.sort((a, b) => {
const aPv = chartUsers.find(user => user.userId === a.id)?.count ?? 0;
const bPv = chartUsers.find(user => user.userId === b.id)?.count ?? 0;
return aPv - bPv;
});
}
return await this.userEntityService.packMany(users, me, { schema: 'UserDetailed' });
});

View file

@ -14,6 +14,7 @@ import { GetterService } from '@/server/api/GetterService.js';
import { CacheService } from '@/core/CacheService.js';
import { isUserRelated } from '@/misc/is-user-related.js';
import { ApiError } from '../../error.js';
import { Packed } from '@/misc/json-schema.js';
export const meta = {
tags: ['users'],
@ -131,10 +132,12 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
const topRepliedUsers = repliedUsersSorted.slice(0, ps.limit);
// Make replies object (includes weights)
const repliesObj = await Promise.all(topRepliedUsers.map(async (user) => ({
const repliesObj = (await Promise.allSettled(topRepliedUsers.map(async (user) => ({
user: await this.userEntityService.pack(user, me, { schema: 'UserDetailed' }),
weight: repliedUsers[user] / peak,
})));
}))))
.filter((result): result is PromiseFulfilledResult<{ user: Packed<'UserDetailed'>; weight: number }> => result.status === 'fulfilled')
.map(result => result.value);
return repliesObj;
});