summaryrefslogtreecommitdiff
path: root/src/server/api/endpoints/users
diff options
context:
space:
mode:
authorsyuilo <Syuilotan@yahoo.co.jp>2021-10-23 01:08:45 +0900
committersyuilo <Syuilotan@yahoo.co.jp>2021-10-23 01:08:45 +0900
commitd0d5068f728e13f3ebe1dc227ddaacf380817ec4 (patch)
tree7bb95207e01bff1bee9877829c0556d3ecf62176 /src/server/api/endpoints/users
parentMerge branch 'develop' (diff)
parent12.93.0 (diff)
downloadmisskey-d0d5068f728e13f3ebe1dc227ddaacf380817ec4.tar.gz
misskey-d0d5068f728e13f3ebe1dc227ddaacf380817ec4.tar.bz2
misskey-d0d5068f728e13f3ebe1dc227ddaacf380817ec4.zip
Merge branch 'develop'
Diffstat (limited to 'src/server/api/endpoints/users')
-rw-r--r--src/server/api/endpoints/users/reactions.ts79
-rw-r--r--src/server/api/endpoints/users/search-by-username-and-host.ts74
-rw-r--r--src/server/api/endpoints/users/search.ts101
3 files changed, 189 insertions, 65 deletions
diff --git a/src/server/api/endpoints/users/reactions.ts b/src/server/api/endpoints/users/reactions.ts
new file mode 100644
index 0000000000..fe5e4d84a9
--- /dev/null
+++ b/src/server/api/endpoints/users/reactions.ts
@@ -0,0 +1,79 @@
+import $ from 'cafy';
+import { ID } from '@/misc/cafy-id';
+import define from '../../define';
+import { NoteReactions, UserProfiles } from '@/models/index';
+import { makePaginationQuery } from '../../common/make-pagination-query';
+import { generateVisibilityQuery } from '../../common/generate-visibility-query';
+import { ApiError } from '../../error';
+
+export const meta = {
+ tags: ['users', 'reactions'],
+
+ requireCredential: false as const,
+
+ params: {
+ userId: {
+ validator: $.type(ID),
+ },
+
+ limit: {
+ validator: $.optional.num.range(1, 100),
+ default: 10,
+ },
+
+ sinceId: {
+ validator: $.optional.type(ID),
+ },
+
+ untilId: {
+ validator: $.optional.type(ID),
+ },
+
+ sinceDate: {
+ validator: $.optional.num,
+ },
+
+ untilDate: {
+ validator: $.optional.num,
+ },
+ },
+
+ res: {
+ type: 'array' as const,
+ optional: false as const, nullable: false as const,
+ items: {
+ type: 'object' as const,
+ optional: false as const, nullable: false as const,
+ ref: 'NoteReaction',
+ }
+ },
+
+ errors: {
+ reactionsNotPublic: {
+ message: 'Reactions of the user is not public.',
+ code: 'REACTIONS_NOT_PUBLIC',
+ id: '673a7dd2-6924-1093-e0c0-e68456ceae5c'
+ },
+ }
+};
+
+export default define(meta, async (ps, me) => {
+ const profile = await UserProfiles.findOneOrFail(ps.userId);
+
+ if (me == null || (me.id !== ps.userId && !profile.publicReactions)) {
+ throw new ApiError(meta.errors.reactionsNotPublic);
+ }
+
+ const query = makePaginationQuery(NoteReactions.createQueryBuilder('reaction'),
+ ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate)
+ .andWhere(`reaction.userId = :userId`, { userId: ps.userId })
+ .leftJoinAndSelect('reaction.note', 'note');
+
+ generateVisibilityQuery(query, me);
+
+ const reactions = await query
+ .take(ps.limit!)
+ .getMany();
+
+ return await Promise.all(reactions.map(reaction => NoteReactions.pack(reaction, me, { withNote: true })));
+});
diff --git a/src/server/api/endpoints/users/search-by-username-and-host.ts b/src/server/api/endpoints/users/search-by-username-and-host.ts
index b9fbf48fb2..1ec5e1a743 100644
--- a/src/server/api/endpoints/users/search-by-username-and-host.ts
+++ b/src/server/api/endpoints/users/search-by-username-and-host.ts
@@ -1,6 +1,9 @@
import $ from 'cafy';
import define from '../../define';
-import { Users } from '@/models/index';
+import { Followings, Users } from '@/models/index';
+import { Brackets } from 'typeorm';
+import { USER_ACTIVE_THRESHOLD } from '@/const';
+import { User } from '@/models/entities/user';
export const meta = {
tags: ['users'],
@@ -16,11 +19,6 @@ export const meta = {
validator: $.optional.nullable.str,
},
- offset: {
- validator: $.optional.num.min(0),
- default: 0,
- },
-
limit: {
validator: $.optional.num.range(1, 100),
default: 10,
@@ -44,43 +42,73 @@ export const meta = {
};
export default define(meta, async (ps, me) => {
+ const activeThreshold = new Date(Date.now() - (1000 * 60 * 60 * 24 * 30)); // 30日
+
if (ps.host) {
const q = Users.createQueryBuilder('user')
.where('user.isSuspended = FALSE')
.andWhere('user.host LIKE :host', { host: ps.host.toLowerCase() + '%' });
if (ps.username) {
- q.andWhere('user.usernameLower like :username', { username: ps.username.toLowerCase() + '%' });
+ q.andWhere('user.usernameLower LIKE :username', { username: ps.username.toLowerCase() + '%' });
}
q.andWhere('user.updatedAt IS NOT NULL');
q.orderBy('user.updatedAt', 'DESC');
- const users = await q.take(ps.limit!).skip(ps.offset).getMany();
+ const users = await q.take(ps.limit!).getMany();
return await Users.packMany(users, me, { detail: ps.detail });
} else if (ps.username) {
- let users = await Users.createQueryBuilder('user')
- .where('user.host IS NULL')
- .andWhere('user.isSuspended = FALSE')
- .andWhere('user.usernameLower like :username', { username: ps.username.toLowerCase() + '%' })
- .andWhere('user.updatedAt IS NOT NULL')
- .orderBy('user.updatedAt', 'DESC')
- .take(ps.limit!)
- .skip(ps.offset)
- .getMany();
+ let users: User[] = [];
- if (users.length < ps.limit!) {
- const otherUsers = await Users.createQueryBuilder('user')
- .where('user.host IS NOT NULL')
+ if (me) {
+ const followingQuery = Followings.createQueryBuilder('following')
+ .select('following.followeeId')
+ .where('following.followerId = :followerId', { followerId: me.id });
+
+ const query = Users.createQueryBuilder('user')
+ .where(`user.id IN (${ followingQuery.getQuery() })`)
+ .andWhere(`user.id != :meId`, { meId: me.id })
.andWhere('user.isSuspended = FALSE')
- .andWhere('user.usernameLower like :username', { username: ps.username.toLowerCase() + '%' })
+ .andWhere('user.usernameLower LIKE :username', { username: ps.username.toLowerCase() + '%' })
+ .andWhere(new Brackets(qb => { qb
+ .where('user.updatedAt IS NULL')
+ .orWhere('user.updatedAt > :activeThreshold', { activeThreshold: activeThreshold });
+ }));
+
+ query.setParameters(followingQuery.getParameters());
+
+ users = await query
+ .orderBy('user.usernameLower', 'ASC')
+ .take(ps.limit!)
+ .getMany();
+
+ if (users.length < ps.limit!) {
+ const otherQuery = await Users.createQueryBuilder('user')
+ .where(`user.id NOT IN (${ followingQuery.getQuery() })`)
+ .andWhere(`user.id != :meId`, { meId: me.id })
+ .andWhere('user.isSuspended = FALSE')
+ .andWhere('user.usernameLower LIKE :username', { username: ps.username.toLowerCase() + '%' })
+ .andWhere('user.updatedAt IS NOT NULL');
+
+ otherQuery.setParameters(followingQuery.getParameters());
+
+ const otherUsers = await otherQuery
+ .orderBy('user.updatedAt', 'DESC')
+ .take(ps.limit! - users.length)
+ .getMany();
+
+ users = users.concat(otherUsers);
+ }
+ } else {
+ users = await Users.createQueryBuilder('user')
+ .where('user.isSuspended = FALSE')
+ .andWhere('user.usernameLower LIKE :username', { username: ps.username.toLowerCase() + '%' })
.andWhere('user.updatedAt IS NOT NULL')
.orderBy('user.updatedAt', 'DESC')
.take(ps.limit! - users.length)
.getMany();
-
- users = users.concat(otherUsers);
}
return await Users.packMany(users, me, { detail: ps.detail });
diff --git a/src/server/api/endpoints/users/search.ts b/src/server/api/endpoints/users/search.ts
index 8011d90b3d..9aa988d9ed 100644
--- a/src/server/api/endpoints/users/search.ts
+++ b/src/server/api/endpoints/users/search.ts
@@ -2,6 +2,7 @@ import $ from 'cafy';
import define from '../../define';
import { UserProfiles, Users } from '@/models/index';
import { User } from '@/models/entities/user';
+import { Brackets } from 'typeorm';
export const meta = {
tags: ['users'],
@@ -23,9 +24,9 @@ export const meta = {
default: 10,
},
- localOnly: {
- validator: $.optional.bool,
- default: false,
+ origin: {
+ validator: $.optional.str.or(['local', 'remote', 'combined']),
+ default: 'combined',
},
detail: {
@@ -46,63 +47,79 @@ export const meta = {
};
export default define(meta, async (ps, me) => {
+ const activeThreshold = new Date(Date.now() - (1000 * 60 * 60 * 24 * 30)); // 30日
+
const isUsername = ps.query.startsWith('@');
let users: User[] = [];
if (isUsername) {
- users = await Users.createQueryBuilder('user')
- .where('user.host IS NULL')
- .andWhere('user.isSuspended = FALSE')
- .andWhere('user.usernameLower like :username', { username: ps.query.replace('@', '').toLowerCase() + '%' })
- .andWhere('user.updatedAt IS NOT NULL')
- .orderBy('user.updatedAt', 'DESC')
+ const usernameQuery = Users.createQueryBuilder('user')
+ .where('user.usernameLower LIKE :username', { username: ps.query.replace('@', '').toLowerCase() + '%' })
+ .andWhere(new Brackets(qb => { qb
+ .where('user.updatedAt IS NULL')
+ .orWhere('user.updatedAt > :activeThreshold', { activeThreshold: activeThreshold });
+ }))
+ .andWhere('user.isSuspended = FALSE');
+
+ if (ps.origin === 'local') {
+ usernameQuery.andWhere('user.host IS NULL');
+ } else if (ps.origin === 'remote') {
+ usernameQuery.andWhere('user.host IS NOT NULL');
+ }
+
+ users = await usernameQuery
+ .orderBy('user.updatedAt', 'DESC', 'NULLS LAST')
.take(ps.limit!)
.skip(ps.offset)
.getMany();
+ } else {
+ const nameQuery = Users.createQueryBuilder('user')
+ .where('user.name ILIKE :query', { query: '%' + ps.query + '%' })
+ .andWhere(new Brackets(qb => { qb
+ .where('user.updatedAt IS NULL')
+ .orWhere('user.updatedAt > :activeThreshold', { activeThreshold: activeThreshold });
+ }))
+ .andWhere('user.isSuspended = FALSE');
- if (users.length < ps.limit! && !ps.localOnly) {
- const otherUsers = await Users.createQueryBuilder('user')
- .where('user.host IS NOT NULL')
- .andWhere('user.isSuspended = FALSE')
- .andWhere('user.usernameLower like :username', { username: ps.query.replace('@', '').toLowerCase() + '%' })
- .andWhere('user.updatedAt IS NOT NULL')
- .orderBy('user.updatedAt', 'DESC')
- .take(ps.limit! - users.length)
- .getMany();
-
- users = users.concat(otherUsers);
+ if (ps.origin === 'local') {
+ nameQuery.andWhere('user.host IS NULL');
+ } else if (ps.origin === 'remote') {
+ nameQuery.andWhere('user.host IS NOT NULL');
}
- } else {
- const profQuery = UserProfiles.createQueryBuilder('prof')
- .select('prof.userId')
- .where('prof.userHost IS NULL')
- .andWhere('prof.description ilike :query', { query: '%' + ps.query + '%' });
- users = await Users.createQueryBuilder('user')
- .where(`user.id IN (${ profQuery.getQuery() })`)
- .setParameters(profQuery.getParameters())
- .andWhere('user.updatedAt IS NOT NULL')
- .orderBy('user.updatedAt', 'DESC')
+ users = await nameQuery
+ .orderBy('user.updatedAt', 'DESC', 'NULLS LAST')
.take(ps.limit!)
.skip(ps.offset)
.getMany();
- if (users.length < ps.limit! && !ps.localOnly) {
- const profQuery2 = UserProfiles.createQueryBuilder('prof')
+ if (users.length < ps.limit!) {
+ const profQuery = UserProfiles.createQueryBuilder('prof')
.select('prof.userId')
- .where('prof.userHost IS NOT NULL')
- .andWhere('prof.description ilike :query', { query: '%' + ps.query + '%' });
+ .where('prof.description ILIKE :query', { query: '%' + ps.query + '%' });
- const otherUsers = await Users.createQueryBuilder('user')
- .where(`user.id IN (${ profQuery2.getQuery() })`)
- .setParameters(profQuery2.getParameters())
- .andWhere('user.updatedAt IS NOT NULL')
- .orderBy('user.updatedAt', 'DESC')
- .take(ps.limit! - users.length)
- .getMany();
+ if (ps.origin === 'local') {
+ profQuery.andWhere('prof.userHost IS NULL');
+ } else if (ps.origin === 'remote') {
+ profQuery.andWhere('prof.userHost IS NOT NULL');
+ }
+
+ const query = Users.createQueryBuilder('user')
+ .where(`user.id IN (${ profQuery.getQuery() })`)
+ .andWhere(new Brackets(qb => { qb
+ .where('user.updatedAt IS NULL')
+ .orWhere('user.updatedAt > :activeThreshold', { activeThreshold: activeThreshold });
+ }))
+ .andWhere('user.isSuspended = FALSE')
+ .setParameters(profQuery.getParameters());
- users = users.concat(otherUsers);
+ users = users.concat(await query
+ .orderBy('user.updatedAt', 'DESC', 'NULLS LAST')
+ .take(ps.limit!)
+ .skip(ps.offset)
+ .getMany()
+ );
}
}