From 4f4d2b7c5389ac73af1198577902379f7920326f Mon Sep 17 00:00:00 2001 From: Johann150 Date: Thu, 23 Jun 2022 14:32:17 +0200 Subject: refactor: simplify ap/show with DbResolver (#8838) Using the existing code in DbResolver we can avoid separate code for parsing the URIs in this endpoint. Co-authored-by: syuilo --- .../backend/src/server/api/endpoints/ap/show.ts | 134 ++++++--------------- 1 file changed, 34 insertions(+), 100 deletions(-) (limited to 'packages/backend/src/server/api') diff --git a/packages/backend/src/server/api/endpoints/ap/show.ts b/packages/backend/src/server/api/endpoints/ap/show.ts index 3c0c0642e3..6442a1412c 100644 --- a/packages/backend/src/server/api/endpoints/ap/show.ts +++ b/packages/backend/src/server/api/endpoints/ap/show.ts @@ -2,12 +2,13 @@ import define from '../../define.js'; import config from '@/config/index.js'; import { createPerson } from '@/remote/activitypub/models/person.js'; import { createNote } from '@/remote/activitypub/models/note.js'; +import DbResolver from '@/remote/activitypub/db-resolver.js'; import Resolver from '@/remote/activitypub/resolver.js'; import { ApiError } from '../../error.js'; import { extractDbHost } from '@/misc/convert-host.js'; import { Users, Notes } from '@/models/index.js'; import { Note } from '@/models/entities/note.js'; -import { User } from '@/models/entities/user.js'; +import { CacheableLocalUser, User } from '@/models/entities/user.js'; import { fetchMeta } from '@/misc/fetch-meta.js'; import { isActor, isPost, getApId } from '@/remote/activitypub/type.js'; import ms from 'ms'; @@ -77,8 +78,8 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps) => { - const object = await fetchAny(ps.uri); +export default define(meta, paramDef, async (ps, me) => { + const object = await fetchAny(ps.uri, me); if (object) { return object; } else { @@ -89,48 +90,18 @@ export default define(meta, paramDef, async (ps) => { /*** * URIからUserかNoteを解決する */ -async function fetchAny(uri: string): Promise | null> { - // URIがこのサーバーを指しているなら、ローカルユーザーIDとしてDBからフェッチ - if (uri.startsWith(config.url + '/')) { - const parts = uri.split('/'); - const id = parts.pop(); - const type = parts.pop(); - - if (type === 'notes') { - const note = await Notes.findOneBy({ id }); - - if (note) { - return { - type: 'Note', - object: await Notes.pack(note, null, { detail: true }), - }; - } - } else if (type === 'users') { - const user = await Users.findOneBy({ id }); - - if (user) { - return { - type: 'User', - object: await Users.pack(user, null, { detail: true }), - }; - } - } - } - +async function fetchAny(uri: string, me: CacheableLocalUser | null | undefined): Promise | null> { // ブロックしてたら中断 const fetchedMeta = await fetchMeta(); if (fetchedMeta.blockedHosts.includes(extractDbHost(uri))) return null; - // URI(AP Object id)としてDB検索 - { - const [user, note] = await Promise.all([ - Users.findOneBy({ uri: uri }), - Notes.findOneBy({ uri: uri }), - ]); + const dbResolver = new DbResolver(); - const packed = await mergePack(user, note); - if (packed !== null) return packed; - } + let local = await mergePack(me, ...await Promise.all([ + dbResolver.getUserFromApId(uri), + dbResolver.getNoteFromApId(uri), + ])); + if (local != null) return local; // リモートから一旦オブジェクトフェッチ const resolver = new Resolver(); @@ -139,74 +110,37 @@ async function fetchAny(uri: string): Promise | n // /@user のような正規id以外で取得できるURIが指定されていた場合、ここで初めて正規URIが確定する // これはDBに存在する可能性があるため再度DB検索 if (uri !== object.id) { - if (object.id.startsWith(config.url + '/')) { - const parts = object.id.split('/'); - const id = parts.pop(); - const type = parts.pop(); - - if (type === 'notes') { - const note = await Notes.findOneBy({ id }); - - if (note) { - return { - type: 'Note', - object: await Notes.pack(note, null, { detail: true }), - }; - } - } else if (type === 'users') { - const user = await Users.findOneBy({ id }); - - if (user) { - return { - type: 'User', - object: await Users.pack(user, null, { detail: true }), - }; - } - } - } - - const [user, note] = await Promise.all([ - Users.findOneBy({ uri: object.id }), - Notes.findOneBy({ uri: object.id }), - ]); - - const packed = await mergePack(user, note); - if (packed !== null) return packed; + local = await mergePack(me, ...await Promise.all([ + dbResolver.getUserFromApId(object.id), + dbResolver.getNoteFromApId(object.id), + ])); + if (local != null) return local; } - // それでもみつからなければ新規であるため登録 - if (isActor(object)) { - const user = await createPerson(getApId(object)); - return { - type: 'User', - object: await Users.pack(user, null, { detail: true }), - }; - } - - if (isPost(object)) { - const note = await createNote(getApId(object), undefined, true); - return { - type: 'Note', - object: await Notes.pack(note!, null, { detail: true }), - }; - } - - return null; + return await mergePack( + me, + isActor(object) ? await createPerson(getApId(object)) : null, + isPost(object) ? await createNote(getApId(object), undefined, true) : null, + ); } -async function mergePack(user: User | null | undefined, note: Note | null | undefined): Promise | null> { +async function mergePack(me: CacheableLocalUser | null | undefined, user: User | null | undefined, note: Note | null | undefined): Promise | null> { if (user != null) { return { type: 'User', - object: await Users.pack(user, null, { detail: true }), - }; - } - - if (note != null) { - return { - type: 'Note', - object: await Notes.pack(note, null, { detail: true }), + object: await Users.pack(user, me, { detail: true }), }; + } else if (note != null) { + try { + const object = await Notes.pack(note, me, { detail: true }); + + return { + type: 'Note', + object, + }; + } catch (e) { + return null; + } } return null; -- cgit v1.2.3-freya From 6f8e3fe36633836d36c5447d78636f370b36cd84 Mon Sep 17 00:00:00 2001 From: MeiMei <30769358+mei23@users.noreply.github.com> Date: Fri, 24 Jun 2022 19:22:19 +0900 Subject: enhance: Redisをioredisに統一してIPv6サポート (#8869) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Use ioredis, Supports IPv6 host https://github.com/misskey-dev/misskey/issues/8862 * Fix import * order * a * i * fix * flushdb * family * CHANGELOG * redis_version Co-authored-by: syuilo --- .config/example.yml | 1 + CHANGELOG.md | 2 ++ packages/backend/package.json | 2 +- packages/backend/src/config/types.ts | 1 + packages/backend/src/db/postgre.ts | 7 ++++--- packages/backend/src/db/redis.ts | 19 +++++++++---------- packages/backend/src/queue/initialize.ts | 1 + .../src/server/api/endpoints/admin/server-info.ts | 6 +++++- packages/backend/yarn.lock | 19 ++----------------- 9 files changed, 26 insertions(+), 32 deletions(-) (limited to 'packages/backend/src/server/api') diff --git a/.config/example.yml b/.config/example.yml index ef91c86f52..8b9d9b4823 100644 --- a/.config/example.yml +++ b/.config/example.yml @@ -57,6 +57,7 @@ db: redis: host: localhost port: 6379 + #family: 0 # 0=Both, 4=IPv4, 6=IPv6 #pass: example-pass #prefix: example-prefix #db: 1 diff --git a/CHANGELOG.md b/CHANGELOG.md index c4486dd6c5..3dc75e6f92 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,8 @@ You should also include the user name that made the change. - Improve player detection in URL preview @mei23 - Add Badge Image to Push Notification #8012 @tamaina - Client: Removing entries from a clip @futchitwo +- Server: Supports IPv6 on Redis transport. @mei23 + IPv4/IPv6 is used by default. You can tune this behavior via `redis.family`. ### Bugfixes - Server: Fix GenerateVideoThumbnail failed @mei23 diff --git a/packages/backend/package.json b/packages/backend/package.json index 3ac5b7398c..b126f723ec 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -47,6 +47,7 @@ "fluent-ffmpeg": "2.1.2", "got": "12.1.0", "hpagent": "0.1.2", + "ioredis": "4.28.5", "ip-cidr": "3.0.10", "is-svg": "4.3.2", "js-yaml": "4.1.0", @@ -86,7 +87,6 @@ "random-seed": "0.3.0", "ratelimiter": "3.4.1", "re2": "1.17.4", - "redis": "3.1.2", "redis-lock": "0.1.4", "reflect-metadata": "0.1.13", "rename": "1.0.4", diff --git a/packages/backend/src/config/types.ts b/packages/backend/src/config/types.ts index 948545db7a..78510c8377 100644 --- a/packages/backend/src/config/types.ts +++ b/packages/backend/src/config/types.ts @@ -19,6 +19,7 @@ export type Source = { redis: { host: string; port: number; + family?: number; pass: string; db?: number; prefix?: string; diff --git a/packages/backend/src/db/postgre.ts b/packages/backend/src/db/postgre.ts index 298f6713ea..904bbb8b7c 100644 --- a/packages/backend/src/db/postgre.ts +++ b/packages/backend/src/db/postgre.ts @@ -192,12 +192,13 @@ export const db = new DataSource({ synchronize: process.env.NODE_ENV === 'test', dropSchema: process.env.NODE_ENV === 'test', cache: !config.db.disableCache ? { - type: 'redis', + type: 'ioredis', options: { host: config.redis.host, port: config.redis.port, + family: config.redis.family == null ? 0 : config.redis.family, password: config.redis.pass, - prefix: `${config.redis.prefix}:query:`, + keyPrefix: `${config.redis.prefix}:query:`, db: config.redis.db || 0, }, } : false, @@ -226,7 +227,7 @@ export async function initDb(force = false) { export async function resetDb() { const reset = async () => { - await redisClient.FLUSHDB(); + await redisClient.flushdb(); const tables = await db.query(`SELECT relname AS "table" FROM pg_class C LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace) WHERE nspname NOT IN ('pg_catalog', 'information_schema') diff --git a/packages/backend/src/db/redis.ts b/packages/backend/src/db/redis.ts index 9346041456..49f5bb2ba8 100644 --- a/packages/backend/src/db/redis.ts +++ b/packages/backend/src/db/redis.ts @@ -1,16 +1,15 @@ -import * as redis from 'redis'; +import Redis from 'ioredis'; import config from '@/config/index.js'; export function createConnection() { - return redis.createClient( - config.redis.port, - config.redis.host, - { - password: config.redis.pass, - prefix: config.redis.prefix, - db: config.redis.db || 0, - } - ); + return new Redis({ + port: config.redis.port, + host: config.redis.host, + family: config.redis.family == null ? 0 : config.redis.family, + password: config.redis.pass, + keyPrefix: `${config.redis.prefix}:`, + db: config.redis.db || 0, + }); } export const subsdcriber = createConnection(); diff --git a/packages/backend/src/queue/initialize.ts b/packages/backend/src/queue/initialize.ts index 1db118ca9f..eef4080af3 100644 --- a/packages/backend/src/queue/initialize.ts +++ b/packages/backend/src/queue/initialize.ts @@ -6,6 +6,7 @@ export function initialize(name: string, limitPerSec = -1) { redis: { port: config.redis.port, host: config.redis.host, + family: config.redis.family == null ? 0 : config.redis.family, password: config.redis.pass, db: config.redis.db || 0, }, diff --git a/packages/backend/src/server/api/endpoints/admin/server-info.ts b/packages/backend/src/server/api/endpoints/admin/server-info.ts index 9c150420b1..85c6fb82e7 100644 --- a/packages/backend/src/server/api/endpoints/admin/server-info.ts +++ b/packages/backend/src/server/api/endpoints/admin/server-info.ts @@ -99,12 +99,16 @@ export default define(meta, paramDef, async () => { const fsStats = await si.fsSize(); const netInterface = await si.networkInterfaceDefault(); + const redisServerInfo = await redisClient.info('Server'); + const m = redisServerInfo.match(new RegExp('^redis_version:(.*)', 'm')); + const redis_version = m?.[1]; + return { machine: os.hostname(), os: os.platform(), node: process.version, psql: await db.query('SHOW server_version').then(x => x[0].server_version), - redis: redisClient.server_info.redis_version, + redis: redis_version, cpu: { model: os.cpus()[0].model, cores: os.cpus().length, diff --git a/packages/backend/yarn.lock b/packages/backend/yarn.lock index 4c89fdf050..aa134183a9 100644 --- a/packages/backend/yarn.lock +++ b/packages/backend/yarn.lock @@ -2227,11 +2227,6 @@ denque@^1.1.0: resolved "https://registry.yarnpkg.com/denque/-/denque-1.4.1.tgz#6744ff7641c148c3f8a69c307e51235c1f4a37cf" integrity sha512-OfzPuSZKGcgr96rf1oODnfjqBFmr1DVoc/TrItj3Ohe0Ah1C5WX5Baquw/9U9KovnQ88EqmJbD66rKYUQYN1tQ== -denque@^1.5.0: - version "1.5.0" - resolved "https://registry.yarnpkg.com/denque/-/denque-1.5.0.tgz#773de0686ff2d8ec2ff92914316a47b73b1c73de" - integrity sha512-CYiCSgIF1p6EUByQPlGkKnP1M9g0ZV3qMIrqMqZqdwazygIA/YP2vrbcyl1h/WppKJTdl1F85cXIle+394iDAQ== - depd@^1.1.2, depd@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" @@ -3581,7 +3576,7 @@ internal-slot@^1.0.3: has "^1.0.3" side-channel "^1.0.4" -ioredis@^4.28.5: +ioredis@4.28.5, ioredis@^4.28.5: version "4.28.5" resolved "https://registry.yarnpkg.com/ioredis/-/ioredis-4.28.5.tgz#5c149e6a8d76a7f8fa8a504ffc85b7d5b6797f9f" integrity sha512-3GYo0GJtLqgNXj4YhrisLaNNvWSNwSS2wS4OELGfGxH8I69+XfNdnmV1AyN+ZqMh0i7eX+SWjrwFKDBDgfBC1A== @@ -5805,7 +5800,7 @@ reconnecting-websocket@^4.4.0: resolved "https://registry.yarnpkg.com/reconnecting-websocket/-/reconnecting-websocket-4.4.0.tgz#3b0e5b96ef119e78a03135865b8bb0af1b948783" integrity sha512-D2E33ceRPga0NvTDhJmphEgJ7FUYF0v4lr1ki0csq06OdlxKfugGzN0dSkxM/NfqCxYELK4KcaTOUOjTV6Dcng== -redis-commands@1.7.0, redis-commands@^1.7.0: +redis-commands@1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/redis-commands/-/redis-commands-1.7.0.tgz#15a6fea2d58281e27b1cd1acfb4b293e278c3a89" integrity sha512-nJWqw3bTFy21hX/CPKHth6sfhZbdiHP6bTawSgQBlKOVRG7EZkfHbbHwQJnrE4vsQf0CMNE+3gJ4Fmm16vdVlQ== @@ -5845,16 +5840,6 @@ redis@*: "@node-redis/search" "^1.0.2" "@node-redis/time-series" "^1.0.1" -redis@3.1.2: - version "3.1.2" - resolved "https://registry.yarnpkg.com/redis/-/redis-3.1.2.tgz#766851117e80653d23e0ed536254677ab647638c" - integrity sha512-grn5KoZLr/qrRQVwoSkmzdbw6pwF+/rwODtrOr6vuBRiR/f3rjSTGupbF90Zpqm2oenix8Do6RV7pYEkGwlKkw== - dependencies: - denque "^1.5.0" - redis-commands "^1.7.0" - redis-errors "^1.2.0" - redis-parser "^3.0.0" - reflect-metadata@0.1.13, reflect-metadata@^0.1.13: version "0.1.13" resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.1.13.tgz#67ae3ca57c972a2aa1642b10fe363fe32d49dc08" -- cgit v1.2.3-freya From a5241379afa71381bf23b59f6e3066cb934b9fe8 Mon Sep 17 00:00:00 2001 From: Johann150 Date: Fri, 24 Jun 2022 12:44:22 +0200 Subject: fix lints --- packages/backend/src/misc/check-word-mute.ts | 2 +- packages/backend/src/remote/activitypub/type.ts | 2 +- packages/backend/src/server/api/define.ts | 1 - packages/backend/src/services/note/polls/update.ts | 1 - packages/client/src/scripts/check-word-mute.ts | 2 +- 5 files changed, 3 insertions(+), 5 deletions(-) (limited to 'packages/backend/src/server/api') diff --git a/packages/backend/src/misc/check-word-mute.ts b/packages/backend/src/misc/check-word-mute.ts index 015a368dfd..d7662820af 100644 --- a/packages/backend/src/misc/check-word-mute.ts +++ b/packages/backend/src/misc/check-word-mute.ts @@ -18,7 +18,7 @@ export async function checkWordMute(note: NoteLike, me: UserLike | null | undefi if (mutedWords.length > 0) { const text = ((note.cw ?? '') + '\n' + (note.text ?? '')).trim(); - if (text == '') return false; + if (text === '') return false; const matched = mutedWords.some(filter => { if (Array.isArray(filter)) { diff --git a/packages/backend/src/remote/activitypub/type.ts b/packages/backend/src/remote/activitypub/type.ts index 5d00481b75..de7eb0ed83 100644 --- a/packages/backend/src/remote/activitypub/type.ts +++ b/packages/backend/src/remote/activitypub/type.ts @@ -201,7 +201,7 @@ export interface IApMention extends IObject { href: string; } -export const isMention = (object: IObject): object is IApMention=> +export const isMention = (object: IObject): object is IApMention => getApType(object) === 'Mention' && typeof object.href === 'string'; diff --git a/packages/backend/src/server/api/define.ts b/packages/backend/src/server/api/define.ts index 1529894341..47dcb44ea8 100644 --- a/packages/backend/src/server/api/define.ts +++ b/packages/backend/src/server/api/define.ts @@ -21,7 +21,6 @@ ajv.addFormat('misskey:id', /^[a-zA-Z0-9]+$/); export default function (meta: T, paramDef: Ps, cb: executor) : (params: any, user: T['requireCredential'] extends true ? CacheableLocalUser : CacheableLocalUser | null, token: AccessToken | null, file?: any) => Promise { - const validate = ajv.compile(paramDef); return (params: any, user: T['requireCredential'] extends true ? CacheableLocalUser : CacheableLocalUser | null, token: AccessToken | null, file?: any) => { diff --git a/packages/backend/src/services/note/polls/update.ts b/packages/backend/src/services/note/polls/update.ts index 43ca3eff4d..68cbb9835a 100644 --- a/packages/backend/src/services/note/polls/update.ts +++ b/packages/backend/src/services/note/polls/update.ts @@ -14,7 +14,6 @@ export async function deliverQuestionUpdate(noteId: Note['id']) { if (user == null) throw new Error('note not found'); if (Users.isLocalUser(user)) { - const content = renderActivity(renderUpdate(await renderNote(note, false), user)); deliverToFollowers(user, content); deliverToRelays(user, content); diff --git a/packages/client/src/scripts/check-word-mute.ts b/packages/client/src/scripts/check-word-mute.ts index aafdd8920a..35d40a6e08 100644 --- a/packages/client/src/scripts/check-word-mute.ts +++ b/packages/client/src/scripts/check-word-mute.ts @@ -5,7 +5,7 @@ export function checkWordMute(note: Record, me: Record if (mutedWords.length > 0) { const text = ((note.cw ?? '') + '\n' + (note.text ?? '')).trim(); - if (text == '') return false; + if (text === '') return false; const matched = mutedWords.some(filter => { if (Array.isArray(filter)) { -- cgit v1.2.3-freya From 696e8add005837481bc40fb3cd7cb9392439e0ac Mon Sep 17 00:00:00 2001 From: syuilo Date: Fri, 24 Jun 2022 21:43:28 +0900 Subject: feat: 管理者が特定ユーザーのアップロードしたファイル一覧を見れるように MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 2 +- .../src/server/api/endpoints/admin/drive/files.ts | 23 +++--- .../client/src/components/file-list-for-admin.vue | 93 ++++++++++++++++++++++ packages/client/src/pages/admin/files.vue | 82 ++----------------- packages/client/src/pages/user-info.vue | 19 ++++- 5 files changed, 133 insertions(+), 86 deletions(-) create mode 100644 packages/client/src/components/file-list-for-admin.vue (limited to 'packages/backend/src/server/api') diff --git a/CHANGELOG.md b/CHANGELOG.md index 3dc75e6f92..bc3e1a4223 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,7 +13,7 @@ You should also include the user name that made the change. ### Improvements - Server: Add rate limit to i/notifications @tamaina -- Client: Improve files page of control panel @syuilo +- Client: Improve control panel @syuilo - Client: Show warning in control panel when there is an unresolved abuse report @syuilo - Improve player detection in URL preview @mei23 - Add Badge Image to Push Notification #8012 @tamaina diff --git a/packages/backend/src/server/api/endpoints/admin/drive/files.ts b/packages/backend/src/server/api/endpoints/admin/drive/files.ts index 119c4db19b..ba32aac431 100644 --- a/packages/backend/src/server/api/endpoints/admin/drive/files.ts +++ b/packages/backend/src/server/api/endpoints/admin/drive/files.ts @@ -1,5 +1,5 @@ -import define from '../../../define.js'; import { DriveFiles } from '@/models/index.js'; +import define from '../../../define.js'; import { makePaginationQuery } from '../../../common/make-pagination-query.js'; export const meta = { @@ -25,8 +25,9 @@ export const paramDef = { limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, sinceId: { type: 'string', format: 'misskey:id' }, untilId: { type: 'string', format: 'misskey:id' }, + userId: { type: 'string', format: 'misskey:id', nullable: true }, type: { type: 'string', nullable: true, pattern: /^[a-zA-Z0-9\/\-*]+$/.toString().slice(1, -1) }, - origin: { type: 'string', enum: ['combined', 'local', 'remote'], default: "local" }, + origin: { type: 'string', enum: ['combined', 'local', 'remote'], default: 'local' }, hostname: { type: 'string', nullable: true, @@ -41,14 +42,18 @@ export const paramDef = { export default define(meta, paramDef, async (ps, me) => { const query = makePaginationQuery(DriveFiles.createQueryBuilder('file'), ps.sinceId, ps.untilId); - if (ps.origin === 'local') { - query.andWhere('file.userHost IS NULL'); - } else if (ps.origin === 'remote') { - query.andWhere('file.userHost IS NOT NULL'); - } + if (ps.userId) { + query.andWhere('file.userId = :userId', { userId: ps.userId }); + } else { + if (ps.origin === 'local') { + query.andWhere('file.userHost IS NULL'); + } else if (ps.origin === 'remote') { + query.andWhere('file.userHost IS NOT NULL'); + } - if (ps.hostname) { - query.andWhere('file.userHost = :hostname', { hostname: ps.hostname }); + if (ps.hostname) { + query.andWhere('file.userHost = :hostname', { hostname: ps.hostname }); + } } if (ps.type) { diff --git a/packages/client/src/components/file-list-for-admin.vue b/packages/client/src/components/file-list-for-admin.vue new file mode 100644 index 0000000000..992b41a4fc --- /dev/null +++ b/packages/client/src/components/file-list-for-admin.vue @@ -0,0 +1,93 @@ + + + + + diff --git a/packages/client/src/pages/admin/files.vue b/packages/client/src/pages/admin/files.vue index 18bf4f9a8c..1d037fc4e4 100644 --- a/packages/client/src/pages/admin/files.vue +++ b/packages/client/src/pages/admin/files.vue @@ -16,32 +16,15 @@ -
+
+ + +
- - - +
@@ -56,9 +39,7 @@ import XHeader from './_header_.vue'; import MkButton from '@/components/ui/button.vue'; import MkInput from '@/components/form/input.vue'; import MkSelect from '@/components/form/select.vue'; -import MkPagination from '@/components/ui/pagination.vue'; -import MkContainer from '@/components/ui/container.vue'; -import MkDriveFileThumbnail from '@/components/drive-file-thumbnail.vue'; +import MkFileListForAdmin from '@/components/file-list-for-admin.vue'; import bytes from '@/filters/bytes'; import * as os from '@/os'; import { i18n } from '@/i18n'; @@ -67,12 +48,14 @@ import { definePageMetadata } from '@/scripts/page-metadata'; let origin = $ref('local'); let type = $ref(null); let searchHost = $ref(''); +let userId = $ref(''); let viewMode = $ref('grid'); const pagination = { endpoint: 'admin/drive/files' as const, limit: 10, params: computed(() => ({ type: (type && type !== '') ? type : null, + userId: (userId && userId !== '') ? userId : null, origin: origin, hostname: (searchHost && searchHost !== '') ? searchHost : null, })), @@ -134,54 +117,5 @@ definePageMetadata(computed(() => ({ diff --git a/packages/client/src/pages/user-info.vue b/packages/client/src/pages/user-info.vue index cc187b9df3..b4c4aedfcd 100644 --- a/packages/client/src/pages/user-info.vue +++ b/packages/client/src/pages/user-info.vue @@ -76,6 +76,9 @@ +
+ +
@@ -105,6 +108,7 @@ import FormButton from '@/components/ui/button.vue'; import MkKeyValue from '@/components/key-value.vue'; import MkSelect from '@/components/form/select.vue'; import FormSuspense from '@/components/form/suspense.vue'; +import MkFileListForAdmin from '@/components/file-list-for-admin.vue'; import * as os from '@/os'; import number from '@/filters/number'; import bytes from '@/filters/bytes'; @@ -127,6 +131,13 @@ let ap = $ref(null); let moderator = $ref(false); let silenced = $ref(false); let suspended = $ref(false); +const filesPagination = { + endpoint: 'admin/drive/files' as const, + limit: 10, + params: computed(() => ({ + userId: props.userId, + })), +}; function createFetcher() { if (iAmModerator) { @@ -244,7 +255,11 @@ const headerTabs = $computed(() => [{ key: 'chart', title: i18n.ts.charts, icon: 'fas fa-chart-simple', -}, { +}, iAmModerator ? { + key: 'files', + title: i18n.ts.files, + icon: 'fas fa-cloud', +} : null, { key: 'ap', title: 'AP', icon: 'fas fa-share-alt', @@ -252,7 +267,7 @@ const headerTabs = $computed(() => [{ key: 'raw', title: 'Raw data', icon: 'fas fa-code', -}]); +}].filter(x => x != null)); definePageMetadata(computed(() => ({ title: user ? acct(user) : i18n.ts.userInfo, -- cgit v1.2.3-freya From 5728350267faa95e171840b7573d767617ce5491 Mon Sep 17 00:00:00 2001 From: Johann150 Date: Sat, 25 Jun 2022 07:23:59 +0200 Subject: fix: always respect instance mutes (#8854) * fix: muted user query also checks instances This way it can be ensured that the instance mute is used everywhere it is required without checking the whole codebase again. Muted users and muted instances should be used together anyways. * fix lint --- .../api/common/generate-muted-instance-query.ts | 40 ------------------- .../server/api/common/generate-muted-user-query.ts | 46 +++++++++++++++++++--- .../src/server/api/endpoints/i/notifications.ts | 16 ++++++-- .../src/server/api/endpoints/notes/children.ts | 8 ++-- .../server/api/endpoints/notes/global-timeline.ts | 12 +++--- .../server/api/endpoints/notes/hybrid-timeline.ts | 8 +--- .../src/server/api/endpoints/notes/timeline.ts | 6 +-- .../src/server/api/endpoints/users/notes.ts | 8 ++-- 8 files changed, 71 insertions(+), 73 deletions(-) delete mode 100644 packages/backend/src/server/api/common/generate-muted-instance-query.ts (limited to 'packages/backend/src/server/api') diff --git a/packages/backend/src/server/api/common/generate-muted-instance-query.ts b/packages/backend/src/server/api/common/generate-muted-instance-query.ts deleted file mode 100644 index 72a6fec68f..0000000000 --- a/packages/backend/src/server/api/common/generate-muted-instance-query.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { User } from '@/models/entities/user.js'; -import { id } from '@/models/id.js'; -import { UserProfiles } from '@/models/index.js'; -import { SelectQueryBuilder, Brackets } from 'typeorm'; - -function createMutesQuery(id: string) { - return UserProfiles.createQueryBuilder('user_profile') - .select('user_profile.mutedInstances') - .where('user_profile.userId = :muterId', { muterId: id }); -} - -export function generateMutedInstanceQuery(q: SelectQueryBuilder, me: { id: User['id'] }) { - const mutingQuery = createMutesQuery(me.id); - - q - .andWhere(new Brackets(qb => { qb - .andWhere('note.userHost IS NULL') - .orWhere(`NOT((${ mutingQuery.getQuery() })::jsonb ? note.userHost)`); - })) - .andWhere(new Brackets(qb => { qb - .where(`note.replyUserHost IS NULL`) - .orWhere(`NOT ((${ mutingQuery.getQuery() })::jsonb ? note.replyUserHost)`); - })) - .andWhere(new Brackets(qb => { qb - .where(`note.renoteUserHost IS NULL`) - .orWhere(`NOT ((${ mutingQuery.getQuery() })::jsonb ? note.renoteUserHost)`); - })); - q.setParameters(mutingQuery.getParameters()); -} - -export function generateMutedInstanceNotificationQuery(q: SelectQueryBuilder, me: { id: User['id'] }) { - const mutingQuery = createMutesQuery(me.id); - - q.andWhere(new Brackets(qb => { qb - .andWhere('notifier.host IS NULL') - .orWhere(`NOT (( ${mutingQuery.getQuery()} )::jsonb ? notifier.host)`); - })); - - q.setParameters(mutingQuery.getParameters()); -} diff --git a/packages/backend/src/server/api/common/generate-muted-user-query.ts b/packages/backend/src/server/api/common/generate-muted-user-query.ts index 79cb3ff894..e276ff2bd5 100644 --- a/packages/backend/src/server/api/common/generate-muted-user-query.ts +++ b/packages/backend/src/server/api/common/generate-muted-user-query.ts @@ -1,6 +1,6 @@ -import { User } from '@/models/entities/user.js'; -import { Mutings } from '@/models/index.js'; import { SelectQueryBuilder, Brackets } from 'typeorm'; +import { User } from '@/models/entities/user.js'; +import { Mutings, UserProfiles } from '@/models/index.js'; export function generateMutedUserQuery(q: SelectQueryBuilder, me: { id: User['id'] }, exclude?: User) { const mutingQuery = Mutings.createQueryBuilder('muting') @@ -11,21 +11,39 @@ export function generateMutedUserQuery(q: SelectQueryBuilder, me: { id: Use mutingQuery.andWhere('muting.muteeId != :excludeId', { excludeId: exclude.id }); } + const mutingInstanceQuery = UserProfiles.createQueryBuilder('user_profile') + .select('user_profile.mutedInstances') + .where('user_profile.userId = :muterId', { muterId: me.id }); + // 投稿の作者をミュートしていない かつ // 投稿の返信先の作者をミュートしていない かつ // 投稿の引用元の作者をミュートしていない q .andWhere(`note.userId NOT IN (${ mutingQuery.getQuery() })`) .andWhere(new Brackets(qb => { qb - .where(`note.replyUserId IS NULL`) + .where('note.replyUserId IS NULL') .orWhere(`note.replyUserId NOT IN (${ mutingQuery.getQuery() })`); })) .andWhere(new Brackets(qb => { qb - .where(`note.renoteUserId IS NULL`) + .where('note.renoteUserId IS NULL') .orWhere(`note.renoteUserId NOT IN (${ mutingQuery.getQuery() })`); + })) + // mute instances + .andWhere(new Brackets(qb => { qb + .andWhere('note.userHost IS NULL') + .orWhere(`NOT ((${ mutingInstanceQuery.getQuery() })::jsonb ? note.userHost)`); + })) + .andWhere(new Brackets(qb => { qb + .where('note.replyUserHost IS NULL') + .orWhere(`NOT ((${ mutingInstanceQuery.getQuery() })::jsonb ? note.replyUserHost)`); + })) + .andWhere(new Brackets(qb => { qb + .where('note.renoteUserHost IS NULL') + .orWhere(`NOT ((${ mutingInstanceQuery.getQuery() })::jsonb ? note.renoteUserHost)`); })); q.setParameters(mutingQuery.getParameters()); + q.setParameters(mutingInstanceQuery.getParameters()); } export function generateMutedUserQueryForUsers(q: SelectQueryBuilder, me: { id: User['id'] }) { @@ -33,8 +51,26 @@ export function generateMutedUserQueryForUsers(q: SelectQueryBuilder, me: { .select('muting.muteeId') .where('muting.muterId = :muterId', { muterId: me.id }); + const mutingInstanceQuery = UserProfiles.createQueryBuilder('user_profile') + .select('user_profile.mutedInstances') + .where('user_profile.userId = :muterId', { muterId: me.id }); + q - .andWhere(`user.id NOT IN (${ mutingQuery.getQuery() })`); + .andWhere(`user.id NOT IN (${ mutingQuery.getQuery() })`) + // mute instances + .andWhere(new Brackets(qb => { qb + .andWhere('note.userHost IS NULL') + .orWhere(`NOT ((${ mutingInstanceQuery.getQuery() })::jsonb ? note.userHost)`); + })) + .andWhere(new Brackets(qb => { qb + .where('note.replyUserHost IS NULL') + .orWhere(`NOT ((${ mutingInstanceQuery.getQuery() })::jsonb ? note.replyUserHost)`); + })) + .andWhere(new Brackets(qb => { qb + .where('note.renoteUserHost IS NULL') + .orWhere(`NOT ((${ mutingInstanceQuery.getQuery() })::jsonb ? note.renoteUserHost)`); + })); q.setParameters(mutingQuery.getParameters()); + q.setParameters(mutingInstanceQuery.getParameters()); } diff --git a/packages/backend/src/server/api/endpoints/i/notifications.ts b/packages/backend/src/server/api/endpoints/i/notifications.ts index 1c31ce7a68..a2249803ee 100644 --- a/packages/backend/src/server/api/endpoints/i/notifications.ts +++ b/packages/backend/src/server/api/endpoints/i/notifications.ts @@ -1,11 +1,10 @@ import { Brackets } from 'typeorm'; -import { Notifications, Followings, Mutings, Users } from '@/models/index.js'; +import { Notifications, Followings, Mutings, Users, UserProfiles } from '@/models/index.js'; import { notificationTypes } from '@/types.js'; import read from '@/services/note/read.js'; import { readNotification } from '../../common/read-notification.js'; import define from '../../define.js'; import { makePaginationQuery } from '../../common/make-pagination-query.js'; -import { generateMutedInstanceNotificationQuery } from '../../common/generate-muted-instance-query.js'; export const meta = { tags: ['account', 'notifications'], @@ -67,6 +66,10 @@ export default define(meta, paramDef, async (ps, user) => { .select('muting.muteeId') .where('muting.muterId = :muterId', { muterId: user.id }); + const mutingInstanceQuery = UserProfiles.createQueryBuilder('user_profile') + .select('user_profile.mutedInstances') + .where('user_profile.userId = :muterId', { muterId: user.id }); + const suspendedQuery = Users.createQueryBuilder('users') .select('users.id') .where('users.isSuspended = TRUE'); @@ -89,14 +92,21 @@ export default define(meta, paramDef, async (ps, user) => { .leftJoinAndSelect('renoteUser.avatar', 'renoteUserAvatar') .leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner'); + // muted users query.andWhere(new Brackets(qb => { qb .where(`notification.notifierId NOT IN (${ mutingQuery.getQuery() })`) .orWhere('notification.notifierId IS NULL'); })); query.setParameters(mutingQuery.getParameters()); - generateMutedInstanceNotificationQuery(query, user); + // muted instances + query.andWhere(new Brackets(qb => { qb + .andWhere('notifier.host IS NULL') + .orWhere(`NOT (( ${mutingInstanceQuery.getQuery()} )::jsonb ? notifier.host)`); + })); + query.setParameters(mutingInstanceQuery.getParameters()); + // suspended users query.andWhere(new Brackets(qb => { qb .where(`notification.notifierId NOT IN (${ suspendedQuery.getQuery() })`) .orWhere('notification.notifierId IS NULL'); diff --git a/packages/backend/src/server/api/endpoints/notes/children.ts b/packages/backend/src/server/api/endpoints/notes/children.ts index 50ba293a58..efc109105c 100644 --- a/packages/backend/src/server/api/endpoints/notes/children.ts +++ b/packages/backend/src/server/api/endpoints/notes/children.ts @@ -5,7 +5,6 @@ import { makePaginationQuery } from '../../common/make-pagination-query.js'; import { generateVisibilityQuery } from '../../common/generate-visibility-query.js'; import { generateMutedUserQuery } from '../../common/generate-muted-user-query.js'; import { generateBlockedUserQuery } from '../../common/generate-block-query.js'; -import { generateMutedInstanceQuery } from '../../common/generate-muted-instance-query.js'; export const meta = { tags: ['notes'], @@ -61,9 +60,10 @@ export default define(meta, paramDef, async (ps, user) => { .leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner'); generateVisibilityQuery(query, user); - if (user) generateMutedUserQuery(query, user); - if (user) generateBlockedUserQuery(query, user); - if (user) generateMutedInstanceQuery(query, user); + if (user) { + generateMutedUserQuery(query, user); + generateBlockedUserQuery(query, user); + } const notes = await query.take(ps.limit).getMany(); diff --git a/packages/backend/src/server/api/endpoints/notes/global-timeline.ts b/packages/backend/src/server/api/endpoints/notes/global-timeline.ts index 418fc62c31..925318f544 100644 --- a/packages/backend/src/server/api/endpoints/notes/global-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/global-timeline.ts @@ -1,11 +1,10 @@ import { fetchMeta } from '@/misc/fetch-meta.js'; -import { Notes, Users } from '@/models/index.js'; +import { Notes } from '@/models/index.js'; import { activeUsersChart } from '@/services/chart/index.js'; import define from '../../define.js'; import { ApiError } from '../../error.js'; import { makePaginationQuery } from '../../common/make-pagination-query.js'; import { generateMutedUserQuery } from '../../common/generate-muted-user-query.js'; -import { generateMutedInstanceQuery } from '../../common/generate-muted-instance-query.js'; import { generateRepliesQuery } from '../../common/generate-replies-query.js'; import { generateMutedNoteQuery } from '../../common/generate-muted-note-query.js'; import { generateBlockedUserQuery } from '../../common/generate-block-query.js'; @@ -76,10 +75,11 @@ export default define(meta, paramDef, async (ps, user) => { .leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner'); generateRepliesQuery(query, user); - if (user) generateMutedUserQuery(query, user); - if (user) generateMutedNoteQuery(query, user); - if (user) generateBlockedUserQuery(query, user); - if (user) generateMutedInstanceQuery(query, user); + if (user) { + generateMutedUserQuery(query, user); + generateMutedNoteQuery(query, user); + generateBlockedUserQuery(query, user); + } if (ps.withFiles) { query.andWhere('note.fileIds != \'{}\''); diff --git a/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts b/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts index 52ee817997..2dc98c4c9f 100644 --- a/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts @@ -1,13 +1,12 @@ import { Brackets } from 'typeorm'; import { fetchMeta } from '@/misc/fetch-meta.js'; -import { Followings, Notes, Users } from '@/models/index.js'; +import { Followings, Notes } from '@/models/index.js'; import { activeUsersChart } from '@/services/chart/index.js'; import define from '../../define.js'; import { ApiError } from '../../error.js'; import { makePaginationQuery } from '../../common/make-pagination-query.js'; import { generateVisibilityQuery } from '../../common/generate-visibility-query.js'; import { generateMutedUserQuery } from '../../common/generate-muted-user-query.js'; -import { generateMutedInstanceQuery } from '../../common/generate-muted-instance-query.js'; import { generateRepliesQuery } from '../../common/generate-replies-query.js'; import { generateMutedNoteQuery } from '../../common/generate-muted-note-query.js'; import { generateChannelQuery } from '../../common/generate-channel-query.js'; @@ -92,7 +91,6 @@ export default define(meta, paramDef, async (ps, user) => { generateRepliesQuery(query, user); generateVisibilityQuery(query, user); generateMutedUserQuery(query, user); - generateMutedInstanceQuery(query, user); generateMutedNoteQuery(query, user); generateBlockedUserQuery(query, user); @@ -134,9 +132,7 @@ export default define(meta, paramDef, async (ps, user) => { const timeline = await query.take(ps.limit).getMany(); process.nextTick(() => { - if (user) { - activeUsersChart.read(user); - } + activeUsersChart.read(user); }); return await Notes.packMany(timeline, user); diff --git a/packages/backend/src/server/api/endpoints/notes/timeline.ts b/packages/backend/src/server/api/endpoints/notes/timeline.ts index d80940e950..22f4925175 100644 --- a/packages/backend/src/server/api/endpoints/notes/timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/timeline.ts @@ -5,7 +5,6 @@ import define from '../../define.js'; import { makePaginationQuery } from '../../common/make-pagination-query.js'; import { generateVisibilityQuery } from '../../common/generate-visibility-query.js'; import { generateMutedUserQuery } from '../../common/generate-muted-user-query.js'; -import { generateMutedInstanceQuery } from '../../common/generate-muted-instance-query.js'; import { generateRepliesQuery } from '../../common/generate-replies-query.js'; import { generateMutedNoteQuery } from '../../common/generate-muted-note-query.js'; import { generateChannelQuery } from '../../common/generate-channel-query.js'; @@ -84,7 +83,6 @@ export default define(meta, paramDef, async (ps, user) => { generateRepliesQuery(query, user); generateVisibilityQuery(query, user); generateMutedUserQuery(query, user); - generateMutedInstanceQuery(query, user); generateMutedNoteQuery(query, user); generateBlockedUserQuery(query, user); @@ -126,9 +124,7 @@ export default define(meta, paramDef, async (ps, user) => { const timeline = await query.take(ps.limit).getMany(); process.nextTick(() => { - if (user) { - activeUsersChart.read(user); - } + activeUsersChart.read(user); }); return await Notes.packMany(timeline, user); diff --git a/packages/backend/src/server/api/endpoints/users/notes.ts b/packages/backend/src/server/api/endpoints/users/notes.ts index aec5c0ea99..9fa56fe83a 100644 --- a/packages/backend/src/server/api/endpoints/users/notes.ts +++ b/packages/backend/src/server/api/endpoints/users/notes.ts @@ -7,7 +7,6 @@ import { makePaginationQuery } from '../../common/make-pagination-query.js'; import { generateVisibilityQuery } from '../../common/generate-visibility-query.js'; import { generateMutedUserQuery } from '../../common/generate-muted-user-query.js'; import { generateBlockedUserQuery } from '../../common/generate-block-query.js'; -import { generateMutedInstanceQuery } from '../../common/generate-muted-instance-query.js'; export const meta = { tags: ['users', 'notes'], @@ -77,9 +76,10 @@ export default define(meta, paramDef, async (ps, me) => { .leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner'); generateVisibilityQuery(query, me); - if (me) generateMutedUserQuery(query, me, user); - if (me) generateBlockedUserQuery(query, me); - if (me) generateMutedInstanceQuery(query, me); + if (me) { + generateMutedUserQuery(query, me, user); + generateBlockedUserQuery(query, me); + } if (ps.withFiles) { query.andWhere('note.fileIds != \'{}\''); -- cgit v1.2.3-freya From 58e83f8e4f634bfd95a3afc847a875413120c301 Mon Sep 17 00:00:00 2001 From: syuilo Date: Sat, 25 Jun 2022 18:26:31 +0900 Subject: feat: allow GET for some endpoints Resolve #8263 --- CHANGELOG.md | 1 + packages/backend/src/server/api/api-handler.ts | 9 +++++- packages/backend/src/server/api/call.ts | 2 +- packages/backend/src/server/api/endpoints.ts | 10 ++++++ .../server/api/endpoints/charts/active-users.ts | 5 ++- .../src/server/api/endpoints/charts/ap-request.ts | 5 ++- .../src/server/api/endpoints/charts/drive.ts | 5 ++- .../src/server/api/endpoints/charts/federation.ts | 5 ++- .../src/server/api/endpoints/charts/hashtag.ts | 5 ++- .../src/server/api/endpoints/charts/instance.ts | 5 ++- .../src/server/api/endpoints/charts/notes.ts | 5 ++- .../src/server/api/endpoints/charts/user/drive.ts | 5 ++- .../server/api/endpoints/charts/user/following.ts | 3 ++ .../src/server/api/endpoints/charts/user/notes.ts | 5 ++- .../server/api/endpoints/charts/user/reactions.ts | 5 ++- .../src/server/api/endpoints/charts/users.ts | 5 ++- packages/backend/src/server/api/index.ts | 19 ++++++++++-- packages/client/src/components/chart.vue | 36 +++++++++++----------- .../client/src/components/instance-card-mini.vue | 2 +- packages/client/src/components/user-card-mini.vue | 2 +- packages/client/src/os.ts | 33 ++++++++++++++++++++ packages/client/src/pages/admin/overview.vue | 4 +-- packages/client/src/widgets/activity.vue | 10 +++--- packages/client/src/widgets/federation.vue | 6 ++-- 24 files changed, 146 insertions(+), 46 deletions(-) (limited to 'packages/backend/src/server/api') diff --git a/CHANGELOG.md b/CHANGELOG.md index bc3e1a4223..8b147ed4b6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ You should also include the user name that made the change. ## 12.x.x (unreleased) ### Improvements +- Server: Allow GET method for some endpoints @syuilo - Server: Add rate limit to i/notifications @tamaina - Client: Improve control panel @syuilo - Client: Show warning in control panel when there is an unresolved abuse report @syuilo diff --git a/packages/backend/src/server/api/api-handler.ts b/packages/backend/src/server/api/api-handler.ts index f97c3dd397..d2d16597e1 100644 --- a/packages/backend/src/server/api/api-handler.ts +++ b/packages/backend/src/server/api/api-handler.ts @@ -6,7 +6,11 @@ import call from './call.js'; import { ApiError } from './error.js'; export default (endpoint: IEndpoint, ctx: Koa.Context) => new Promise((res) => { - const body = ctx.request.body; + const body = ctx.is('multipart/form-data') + ? (ctx.req as any).body + : ctx.method === 'GET' + ? ctx.query + : ctx.request.body; const reply = (x?: any, y?: ApiError) => { if (x == null) { @@ -33,6 +37,9 @@ export default (endpoint: IEndpoint, ctx: Koa.Context) => new Promise((res authenticate(body['i']).then(([user, app]) => { // API invoking call(endpoint.name, user, app, body, ctx).then((res: any) => { + if (ctx.method === 'GET' && endpoint.meta.cacheSec && !body['i'] && !user) { + ctx.set('Cache-Control', `public, max-age=${endpoint.meta.cacheSec}`); + } reply(res); }).catch((e: ApiError) => { reply(e.httpStatusCode ? e.httpStatusCode : e.kind === 'client' ? 400 : 500, e); diff --git a/packages/backend/src/server/api/call.ts b/packages/backend/src/server/api/call.ts index 46afde4e47..75bbc9f908 100644 --- a/packages/backend/src/server/api/call.ts +++ b/packages/backend/src/server/api/call.ts @@ -94,7 +94,7 @@ export default async (endpoint: string, user: CacheableLocalUser | null | undefi } // Cast non JSON input - if (ep.meta.requireFile && ep.params.properties) { + if ((ep.meta.requireFile || ctx?.method === 'GET') && ep.params.properties) { for (const k of Object.keys(ep.params.properties)) { const param = ep.params.properties![k]; if (['boolean', 'number', 'integer'].includes(param.type ?? '') && typeof data[k] === 'string') { diff --git a/packages/backend/src/server/api/endpoints.ts b/packages/backend/src/server/api/endpoints.ts index 5fac7df239..11d9d7c026 100644 --- a/packages/backend/src/server/api/endpoints.ts +++ b/packages/backend/src/server/api/endpoints.ts @@ -701,6 +701,16 @@ export interface IEndpointMeta { readonly kind?: string; readonly description?: string; + + /** + * GETでのリクエストを許容するか否か + */ + readonly allowGet?: boolean; + + /** + * 正常応答をキャッシュ (Cache-Control: public) する秒数 + */ + readonly cacheSec?: number; } export interface IEndpoint { diff --git a/packages/backend/src/server/api/endpoints/charts/active-users.ts b/packages/backend/src/server/api/endpoints/charts/active-users.ts index 97f7885dbe..ea23794296 100644 --- a/packages/backend/src/server/api/endpoints/charts/active-users.ts +++ b/packages/backend/src/server/api/endpoints/charts/active-users.ts @@ -1,11 +1,14 @@ -import define from '../../define.js'; import { getJsonSchema } from '@/services/chart/core.js'; import { activeUsersChart } from '@/services/chart/index.js'; +import define from '../../define.js'; export const meta = { tags: ['charts', 'users'], res: getJsonSchema(activeUsersChart.schema), + + allowGet: true, + cacheSec: 60 * 60, } as const; export const paramDef = { diff --git a/packages/backend/src/server/api/endpoints/charts/ap-request.ts b/packages/backend/src/server/api/endpoints/charts/ap-request.ts index 4477bfc987..06dee250ee 100644 --- a/packages/backend/src/server/api/endpoints/charts/ap-request.ts +++ b/packages/backend/src/server/api/endpoints/charts/ap-request.ts @@ -1,11 +1,14 @@ -import define from '../../define.js'; import { getJsonSchema } from '@/services/chart/core.js'; import { apRequestChart } from '@/services/chart/index.js'; +import define from '../../define.js'; export const meta = { tags: ['charts'], res: getJsonSchema(apRequestChart.schema), + + allowGet: true, + cacheSec: 60 * 60, } as const; export const paramDef = { diff --git a/packages/backend/src/server/api/endpoints/charts/drive.ts b/packages/backend/src/server/api/endpoints/charts/drive.ts index fd6033392f..dd2c2d6838 100644 --- a/packages/backend/src/server/api/endpoints/charts/drive.ts +++ b/packages/backend/src/server/api/endpoints/charts/drive.ts @@ -1,11 +1,14 @@ -import define from '../../define.js'; import { getJsonSchema } from '@/services/chart/core.js'; import { driveChart } from '@/services/chart/index.js'; +import define from '../../define.js'; export const meta = { tags: ['charts', 'drive'], res: getJsonSchema(driveChart.schema), + + allowGet: true, + cacheSec: 60 * 60, } as const; export const paramDef = { diff --git a/packages/backend/src/server/api/endpoints/charts/federation.ts b/packages/backend/src/server/api/endpoints/charts/federation.ts index f842f574ec..8c35b3c46d 100644 --- a/packages/backend/src/server/api/endpoints/charts/federation.ts +++ b/packages/backend/src/server/api/endpoints/charts/federation.ts @@ -1,11 +1,14 @@ -import define from '../../define.js'; import { getJsonSchema } from '@/services/chart/core.js'; import { federationChart } from '@/services/chart/index.js'; +import define from '../../define.js'; export const meta = { tags: ['charts'], res: getJsonSchema(federationChart.schema), + + allowGet: true, + cacheSec: 60 * 60, } as const; export const paramDef = { diff --git a/packages/backend/src/server/api/endpoints/charts/hashtag.ts b/packages/backend/src/server/api/endpoints/charts/hashtag.ts index 01407defdd..77e24a62c3 100644 --- a/packages/backend/src/server/api/endpoints/charts/hashtag.ts +++ b/packages/backend/src/server/api/endpoints/charts/hashtag.ts @@ -1,11 +1,14 @@ -import define from '../../define.js'; import { getJsonSchema } from '@/services/chart/core.js'; import { hashtagChart } from '@/services/chart/index.js'; +import define from '../../define.js'; export const meta = { tags: ['charts', 'hashtags'], res: getJsonSchema(hashtagChart.schema), + + allowGet: true, + cacheSec: 60 * 60, } as const; export const paramDef = { diff --git a/packages/backend/src/server/api/endpoints/charts/instance.ts b/packages/backend/src/server/api/endpoints/charts/instance.ts index 2d12951c6c..817d51ad01 100644 --- a/packages/backend/src/server/api/endpoints/charts/instance.ts +++ b/packages/backend/src/server/api/endpoints/charts/instance.ts @@ -1,11 +1,14 @@ -import define from '../../define.js'; import { getJsonSchema } from '@/services/chart/core.js'; import { instanceChart } from '@/services/chart/index.js'; +import define from '../../define.js'; export const meta = { tags: ['charts'], res: getJsonSchema(instanceChart.schema), + + allowGet: true, + cacheSec: 60 * 60, } as const; export const paramDef = { diff --git a/packages/backend/src/server/api/endpoints/charts/notes.ts b/packages/backend/src/server/api/endpoints/charts/notes.ts index b6089f67ef..951adf5408 100644 --- a/packages/backend/src/server/api/endpoints/charts/notes.ts +++ b/packages/backend/src/server/api/endpoints/charts/notes.ts @@ -1,11 +1,14 @@ -import define from '../../define.js'; import { getJsonSchema } from '@/services/chart/core.js'; import { notesChart } from '@/services/chart/index.js'; +import define from '../../define.js'; export const meta = { tags: ['charts', 'notes'], res: getJsonSchema(notesChart.schema), + + allowGet: true, + cacheSec: 60 * 60, } as const; export const paramDef = { diff --git a/packages/backend/src/server/api/endpoints/charts/user/drive.ts b/packages/backend/src/server/api/endpoints/charts/user/drive.ts index e5db7131a8..f165b40224 100644 --- a/packages/backend/src/server/api/endpoints/charts/user/drive.ts +++ b/packages/backend/src/server/api/endpoints/charts/user/drive.ts @@ -1,11 +1,14 @@ -import define from '../../../define.js'; import { getJsonSchema } from '@/services/chart/core.js'; import { perUserDriveChart } from '@/services/chart/index.js'; +import define from '../../../define.js'; export const meta = { tags: ['charts', 'drive', 'users'], res: getJsonSchema(perUserDriveChart.schema), + + allowGet: true, + cacheSec: 60 * 60, } as const; export const paramDef = { diff --git a/packages/backend/src/server/api/endpoints/charts/user/following.ts b/packages/backend/src/server/api/endpoints/charts/user/following.ts index 9b72de745d..f5d42e21c2 100644 --- a/packages/backend/src/server/api/endpoints/charts/user/following.ts +++ b/packages/backend/src/server/api/endpoints/charts/user/following.ts @@ -6,6 +6,9 @@ export const meta = { tags: ['charts', 'users', 'following'], res: getJsonSchema(perUserFollowingChart.schema), + + allowGet: true, + cacheSec: 60 * 60, } as const; export const paramDef = { diff --git a/packages/backend/src/server/api/endpoints/charts/user/notes.ts b/packages/backend/src/server/api/endpoints/charts/user/notes.ts index 7cc6cbf316..aefe550d43 100644 --- a/packages/backend/src/server/api/endpoints/charts/user/notes.ts +++ b/packages/backend/src/server/api/endpoints/charts/user/notes.ts @@ -1,11 +1,14 @@ -import define from '../../../define.js'; import { getJsonSchema } from '@/services/chart/core.js'; import { perUserNotesChart } from '@/services/chart/index.js'; +import define from '../../../define.js'; export const meta = { tags: ['charts', 'users', 'notes'], res: getJsonSchema(perUserNotesChart.schema), + + allowGet: true, + cacheSec: 60 * 60, } as const; export const paramDef = { diff --git a/packages/backend/src/server/api/endpoints/charts/user/reactions.ts b/packages/backend/src/server/api/endpoints/charts/user/reactions.ts index 5c58a7f152..6bc6b56bf0 100644 --- a/packages/backend/src/server/api/endpoints/charts/user/reactions.ts +++ b/packages/backend/src/server/api/endpoints/charts/user/reactions.ts @@ -1,11 +1,14 @@ -import define from '../../../define.js'; import { getJsonSchema } from '@/services/chart/core.js'; import { perUserReactionsChart } from '@/services/chart/index.js'; +import define from '../../../define.js'; export const meta = { tags: ['charts', 'users', 'reactions'], res: getJsonSchema(perUserReactionsChart.schema), + + allowGet: true, + cacheSec: 60 * 60, } as const; export const paramDef = { diff --git a/packages/backend/src/server/api/endpoints/charts/users.ts b/packages/backend/src/server/api/endpoints/charts/users.ts index 49c762b2e4..338e8fd338 100644 --- a/packages/backend/src/server/api/endpoints/charts/users.ts +++ b/packages/backend/src/server/api/endpoints/charts/users.ts @@ -1,11 +1,14 @@ -import define from '../../define.js'; import { getJsonSchema } from '@/services/chart/core.js'; import { usersChart } from '@/services/chart/index.js'; +import define from '../../define.js'; export const meta = { tags: ['charts', 'users'], res: getJsonSchema(usersChart.schema), + + allowGet: true, + cacheSec: 60 * 60, } as const; export const paramDef = { diff --git a/packages/backend/src/server/api/index.ts b/packages/backend/src/server/api/index.ts index 02bec31b17..83ece51f51 100644 --- a/packages/backend/src/server/api/index.ts +++ b/packages/backend/src/server/api/index.ts @@ -8,6 +8,8 @@ import multer from '@koa/multer'; import bodyParser from 'koa-bodyparser'; import cors from '@koa/cors'; +import { Instances, AccessTokens, Users } from '@/models/index.js'; +import config from '@/config/index.js'; import endpoints from './endpoints.js'; import handler from './api-handler.js'; import signup from './private/signup.js'; @@ -16,8 +18,6 @@ import signupPending from './private/signup-pending.js'; import discord from './service/discord.js'; import github from './service/github.js'; import twitter from './service/twitter.js'; -import { Instances, AccessTokens, Users } from '@/models/index.js'; -import config from '@/config/index.js'; // Init app const app = new Koa(); @@ -56,11 +56,24 @@ for (const endpoint of endpoints) { if (endpoint.meta.requireFile) { router.post(`/${endpoint.name}`, upload.single('file'), handler.bind(null, endpoint)); } else { + // 後方互換性のため if (endpoint.name.includes('-')) { - // 後方互換性のため router.post(`/${endpoint.name.replace(/-/g, '_')}`, handler.bind(null, endpoint)); + + if (endpoint.meta.allowGet) { + router.get(`/${endpoint.name.replace(/-/g, '_')}`, handler.bind(null, endpoint)); + } else { + router.get(`/${endpoint.name.replace(/-/g, '_')}`, async ctx => { ctx.status = 405; }); + } } + router.post(`/${endpoint.name}`, handler.bind(null, endpoint)); + + if (endpoint.meta.allowGet) { + router.get(`/${endpoint.name}`, handler.bind(null, endpoint)); + } else { + router.get(`/${endpoint.name}`, async ctx => { ctx.status = 405; }); + } } } diff --git a/packages/client/src/components/chart.vue b/packages/client/src/components/chart.vue index d9075dd077..fc7c4ff950 100644 --- a/packages/client/src/components/chart.vue +++ b/packages/client/src/components/chart.vue @@ -342,7 +342,7 @@ const exportData = () => { }; const fetchFederationChart = async (): Promise => { - const raw = await os.api('charts/federation', { limit: props.limit, span: props.span }); + const raw = await os.apiGet('charts/federation', { limit: props.limit, span: props.span }); return { series: [{ name: 'Received', @@ -392,7 +392,7 @@ const fetchFederationChart = async (): Promise => { }; const fetchApRequestChart = async (): Promise => { - const raw = await os.api('charts/ap-request', { limit: props.limit, span: props.span }); + const raw = await os.apiGet('charts/ap-request', { limit: props.limit, span: props.span }); return { series: [{ name: 'In', @@ -414,7 +414,7 @@ const fetchApRequestChart = async (): Promise => { }; const fetchNotesChart = async (type: string): Promise => { - const raw = await os.api('charts/notes', { limit: props.limit, span: props.span }); + const raw = await os.apiGet('charts/notes', { limit: props.limit, span: props.span }); return { series: [{ name: 'All', @@ -461,7 +461,7 @@ const fetchNotesChart = async (type: string): Promise => { }; const fetchNotesTotalChart = async (): Promise => { - const raw = await os.api('charts/notes', { limit: props.limit, span: props.span }); + const raw = await os.apiGet('charts/notes', { limit: props.limit, span: props.span }); return { series: [{ name: 'Combined', @@ -480,7 +480,7 @@ const fetchNotesTotalChart = async (): Promise => { }; const fetchUsersChart = async (total: boolean): Promise => { - const raw = await os.api('charts/users', { limit: props.limit, span: props.span }); + const raw = await os.apiGet('charts/users', { limit: props.limit, span: props.span }); return { series: [{ name: 'Combined', @@ -508,7 +508,7 @@ const fetchUsersChart = async (total: boolean): Promise => { }; const fetchActiveUsersChart = async (): Promise => { - const raw = await os.api('charts/active-users', { limit: props.limit, span: props.span }); + const raw = await os.apiGet('charts/active-users', { limit: props.limit, span: props.span }); return { series: [{ name: 'Read & Write', @@ -560,7 +560,7 @@ const fetchActiveUsersChart = async (): Promise => { }; const fetchDriveChart = async (): Promise => { - const raw = await os.api('charts/drive', { limit: props.limit, span: props.span }); + const raw = await os.apiGet('charts/drive', { limit: props.limit, span: props.span }); return { bytes: true, series: [{ @@ -596,7 +596,7 @@ const fetchDriveChart = async (): Promise => { }; const fetchDriveFilesChart = async (): Promise => { - const raw = await os.api('charts/drive', { limit: props.limit, span: props.span }); + const raw = await os.apiGet('charts/drive', { limit: props.limit, span: props.span }); return { series: [{ name: 'All', @@ -631,7 +631,7 @@ const fetchDriveFilesChart = async (): Promise => { }; const fetchInstanceRequestsChart = async (): Promise => { - const raw = await os.api('charts/instance', { host: props.args.host, limit: props.limit, span: props.span }); + const raw = await os.apiGet('charts/instance', { host: props.args.host, limit: props.limit, span: props.span }); return { series: [{ name: 'In', @@ -653,7 +653,7 @@ const fetchInstanceRequestsChart = async (): Promise => { }; const fetchInstanceUsersChart = async (total: boolean): Promise => { - const raw = await os.api('charts/instance', { host: props.args.host, limit: props.limit, span: props.span }); + const raw = await os.apiGet('charts/instance', { host: props.args.host, limit: props.limit, span: props.span }); return { series: [{ name: 'Users', @@ -668,7 +668,7 @@ const fetchInstanceUsersChart = async (total: boolean): Promise => { - const raw = await os.api('charts/instance', { host: props.args.host, limit: props.limit, span: props.span }); + const raw = await os.apiGet('charts/instance', { host: props.args.host, limit: props.limit, span: props.span }); return { series: [{ name: 'Notes', @@ -683,7 +683,7 @@ const fetchInstanceNotesChart = async (total: boolean): Promise => { - const raw = await os.api('charts/instance', { host: props.args.host, limit: props.limit, span: props.span }); + const raw = await os.apiGet('charts/instance', { host: props.args.host, limit: props.limit, span: props.span }); return { series: [{ name: 'Following', @@ -706,7 +706,7 @@ const fetchInstanceFfChart = async (total: boolean): Promise = }; const fetchInstanceDriveUsageChart = async (total: boolean): Promise => { - const raw = await os.api('charts/instance', { host: props.args.host, limit: props.limit, span: props.span }); + const raw = await os.apiGet('charts/instance', { host: props.args.host, limit: props.limit, span: props.span }); return { bytes: true, series: [{ @@ -722,7 +722,7 @@ const fetchInstanceDriveUsageChart = async (total: boolean): Promise => { - const raw = await os.api('charts/instance', { host: props.args.host, limit: props.limit, span: props.span }); + const raw = await os.apiGet('charts/instance', { host: props.args.host, limit: props.limit, span: props.span }); return { series: [{ name: 'Drive files', @@ -737,7 +737,7 @@ const fetchInstanceDriveFilesChart = async (total: boolean): Promise => { - const raw = await os.api('charts/user/notes', { userId: props.args.user.id, limit: props.limit, span: props.span }); + const raw = await os.apiGet('charts/user/notes', { userId: props.args.user.id, limit: props.limit, span: props.span }); return { series: [...(props.args.withoutAll ? [] : [{ name: 'All', @@ -769,7 +769,7 @@ const fetchPerUserNotesChart = async (): Promise => { }; const fetchPerUserFollowingChart = async (): Promise => { - const raw = await os.api('charts/user/following', { userId: props.args.user.id, limit: props.limit, span: props.span }); + const raw = await os.apiGet('charts/user/following', { userId: props.args.user.id, limit: props.limit, span: props.span }); return { series: [{ name: 'Local', @@ -784,7 +784,7 @@ const fetchPerUserFollowingChart = async (): Promise => { }; const fetchPerUserFollowersChart = async (): Promise => { - const raw = await os.api('charts/user/following', { userId: props.args.user.id, limit: props.limit, span: props.span }); + const raw = await os.apiGet('charts/user/following', { userId: props.args.user.id, limit: props.limit, span: props.span }); return { series: [{ name: 'Local', @@ -799,7 +799,7 @@ const fetchPerUserFollowersChart = async (): Promise => { }; const fetchPerUserDriveChart = async (): Promise => { - const raw = await os.api('charts/user/drive', { userId: props.args.user.id, limit: props.limit, span: props.span }); + const raw = await os.apiGet('charts/user/drive', { userId: props.args.user.id, limit: props.limit, span: props.span }); return { series: [{ name: 'Inc', diff --git a/packages/client/src/components/instance-card-mini.vue b/packages/client/src/components/instance-card-mini.vue index 0bac6c0ad0..de16dacf7b 100644 --- a/packages/client/src/components/instance-card-mini.vue +++ b/packages/client/src/components/instance-card-mini.vue @@ -20,7 +20,7 @@ const props = defineProps<{ const chart = $ref(null); -os.api('charts/instance', { host: props.instance.host, limit: 16, span: 'day' }).then(res => { +os.apiGet('charts/instance', { host: props.instance.host, limit: 16, span: 'day' }).then(res => { chart = res; }); diff --git a/packages/client/src/components/user-card-mini.vue b/packages/client/src/components/user-card-mini.vue index d83e4a56f5..2c3e0fe956 100644 --- a/packages/client/src/components/user-card-mini.vue +++ b/packages/client/src/components/user-card-mini.vue @@ -21,7 +21,7 @@ const props = defineProps<{ const chart = $ref(null); -os.api('charts/user/notes', { userId: props.user.id, limit: 16, span: 'day' }).then(res => { +os.apiGet('charts/user/notes', { userId: props.user.id, limit: 16, span: 'day' }).then(res => { chart = res; }); diff --git a/packages/client/src/os.ts b/packages/client/src/os.ts index e823d3719c..00dae867d6 100644 --- a/packages/client/src/os.ts +++ b/packages/client/src/os.ts @@ -52,6 +52,39 @@ export const api = ((endpoint: string, data: Record = {}, token?: s return promise; }) as typeof apiClient.request; +export const apiGet = ((endpoint: string, data: Record = {}) => { + pendingApiRequestsCount.value++; + + const onFinally = () => { + pendingApiRequestsCount.value--; + }; + + const query = new URLSearchParams(data); + + const promise = new Promise((resolve, reject) => { + // Send request + fetch(`${apiUrl}/${endpoint}?${query}`, { + method: 'GET', + credentials: 'omit', + cache: 'default', + }).then(async (res) => { + const body = res.status === 204 ? null : await res.json(); + + if (res.status === 200) { + resolve(body); + } else if (res.status === 204) { + resolve(); + } else { + reject(body.error); + } + }).catch(reject); + }); + + promise.then(onFinally, onFinally); + + return promise; +}) as typeof apiClient.request; + export const apiWithDialog = (( endpoint: string, data: Record = {}, diff --git a/packages/client/src/pages/admin/overview.vue b/packages/client/src/pages/admin/overview.vue index 82b3c33852..f81f6104c7 100644 --- a/packages/client/src/pages/admin/overview.vue +++ b/packages/client/src/pages/admin/overview.vue @@ -90,11 +90,11 @@ onMounted(async () => { os.api('stats', {}).then(statsResponse => { stats = statsResponse; - os.api('charts/users', { limit: 2, span: 'day' }).then(chart => { + os.apiGet('charts/users', { limit: 2, span: 'day' }).then(chart => { usersComparedToThePrevDay = stats.originalUsersCount - chart.local.total[1]; }); - os.api('charts/notes', { limit: 2, span: 'day' }).then(chart => { + os.apiGet('charts/notes', { limit: 2, span: 'day' }).then(chart => { notesComparedToThePrevDay = stats.originalNotesCount - chart.local.total[1]; }); }); diff --git a/packages/client/src/widgets/activity.vue b/packages/client/src/widgets/activity.vue index 7fb9f5894c..265bde4a3b 100644 --- a/packages/client/src/widgets/activity.vue +++ b/packages/client/src/widgets/activity.vue @@ -15,12 +15,12 @@ diff --git a/packages/client/src/pages/admin/overview.pie.vue b/packages/client/src/pages/admin/overview.pie.vue index d14b3cc6db..41a5e53ae3 100644 --- a/packages/client/src/pages/admin/overview.pie.vue +++ b/packages/client/src/pages/admin/overview.pie.vue @@ -64,21 +64,21 @@ onMounted(() => { labels: props.data.map(x => x.name), datasets: [{ backgroundColor: props.data.map(x => x.color), + borderWidth: 0, + spacing: 4, + hoverOffset: 4, data: props.data.map(x => x.value), }], }, options: { layout: { padding: { - left: 8, - right: 8, - top: 8, - bottom: 8, + left: 16, + right: 16, + top: 16, + bottom: 16, }, }, - interaction: { - intersect: false, - }, plugins: { legend: { display: false, diff --git a/packages/client/src/pages/admin/overview.vue b/packages/client/src/pages/admin/overview.vue index 6ccee8aea2..393ee66452 100644 --- a/packages/client/src/pages/admin/overview.vue +++ b/packages/client/src/pages/admin/overview.vue @@ -123,12 +123,12 @@
Sub
- +
Top 10
Pub
- +
Top 10
@@ -411,7 +411,7 @@ onMounted(async () => { federationSubActiveDiff = chart.subActive[0] - chart.subActive[1]; }); - os.apiGet('federation/stats').then(res => { + os.apiGet('federation/stats', { limit: 10 }).then(res => { fedStats = res; }); -- cgit v1.2.3-freya From 66b27bdc9765088468c216ff98015e1fadf77732 Mon Sep 17 00:00:00 2001 From: Johann150 Date: Thu, 30 Jun 2022 22:03:04 +0200 Subject: fix typo Co-authored-by: mei23 --- packages/backend/src/server/api/stream/channels/local-timeline.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'packages/backend/src/server/api') diff --git a/packages/backend/src/server/api/stream/channels/local-timeline.ts b/packages/backend/src/server/api/stream/channels/local-timeline.ts index 8bb9279878..f01f477238 100644 --- a/packages/backend/src/server/api/stream/channels/local-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/local-timeline.ts @@ -51,7 +51,7 @@ export default class extends Channel { } // 流れてきたNoteがミュートしているユーザーが関わるものだったら無視する - if (iUserRelated(note, this.muting)) return; + if (isUserRelated(note, this.muting)) return; // 流れてきたNoteがブロックされているユーザーが関わるものだったら無視する if (isUserRelated(note, this.blocking)) return; -- cgit v1.2.3-freya From b773d516d3a604fb8506c0b5ee449e2461024efa Mon Sep 17 00:00:00 2001 From: syuilo Date: Sat, 2 Jul 2022 12:22:52 +0900 Subject: chore(client): tweak ui --- locales/ja-JP.yml | 1 + packages/backend/src/server/api/endpoints/admin/show-user.ts | 3 ++- packages/client/src/pages/user-info.vue | 10 +++++++++- 3 files changed, 12 insertions(+), 2 deletions(-) (limited to 'packages/backend/src/server/api') diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 1f52c2c259..b97b64dc59 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -861,6 +861,7 @@ document: "ドキュメント" numberOfPageCache: "ページキャッシュ数" numberOfPageCacheDescription: "多くすると利便性が向上しますが、負荷とメモリ使用量が増えます。" logoutConfirm: "ログアウトしますか?" +lastActiveDate: "最終利用日時" _emailUnavailable: used: "既に使用されています" diff --git a/packages/backend/src/server/api/endpoints/admin/show-user.ts b/packages/backend/src/server/api/endpoints/admin/show-user.ts index 78033aed58..36384c2b3d 100644 --- a/packages/backend/src/server/api/endpoints/admin/show-user.ts +++ b/packages/backend/src/server/api/endpoints/admin/show-user.ts @@ -25,7 +25,7 @@ export const paramDef = { export default define(meta, paramDef, async (ps, me) => { const [user, profile] = await Promise.all([ Users.findOneBy({ id: ps.userId }), - UserProfiles.findOneBy({ userId: ps.userId }) + UserProfiles.findOneBy({ userId: ps.userId }), ]); if (user == null || profile == null) { @@ -68,6 +68,7 @@ export default define(meta, paramDef, async (ps, me) => { isModerator: user.isModerator, isSilenced: user.isSilenced, isSuspended: user.isSuspended, + lastActiveDate: user.lastActiveDate, signins, }; }); diff --git a/packages/client/src/pages/user-info.vue b/packages/client/src/pages/user-info.vue index 76b772ece2..b3292290ea 100644 --- a/packages/client/src/pages/user-info.vue +++ b/packages/client/src/pages/user-info.vue @@ -1,7 +1,7 @@ @@ -92,6 +93,7 @@ const host = toUnicode(config.host); let hcaptcha = $ref(); let recaptcha = $ref(); +let turnstile = $ref(); let username: string = $ref(''); let password: string = $ref(''); @@ -106,12 +108,14 @@ let submitting: boolean = $ref(false); let ToSAgreement: boolean = $ref(false); let hCaptchaResponse = $ref(null); let reCaptchaResponse = $ref(null); +let turnstileResponse = $ref(null); const shouldDisableSubmitting = $computed((): boolean => { return submitting || instance.tosUrl && !ToSAgreement || instance.enableHcaptcha && !hCaptchaResponse || instance.enableRecaptcha && !reCaptchaResponse || + instance.enableTurnstile && !turnstileResponse || passwordRetypeState === 'not-match'; }); @@ -198,6 +202,7 @@ function onSubmit(): void { invitationCode, 'hcaptcha-response': hCaptchaResponse, 'g-recaptcha-response': reCaptchaResponse, + 'turnstile-response': turnstileResponse, }).then(() => { if (instance.emailRequiredForSignup) { os.alert({ @@ -222,6 +227,7 @@ function onSubmit(): void { submitting = false; hcaptcha.reset?.(); recaptcha.reset?.(); + turnstile.reset?.(); os.alert({ type: 'error', diff --git a/packages/client/src/pages/admin/bot-protection.vue b/packages/client/src/pages/admin/bot-protection.vue index 72d5e379de..484a9d1a1a 100644 --- a/packages/client/src/pages/admin/bot-protection.vue +++ b/packages/client/src/pages/admin/bot-protection.vue @@ -6,6 +6,7 @@ + + {{ i18n.ts.save }}
@@ -61,6 +76,8 @@ let hcaptchaSiteKey: string | null = $ref(null); let hcaptchaSecretKey: string | null = $ref(null); let recaptchaSiteKey: string | null = $ref(null); let recaptchaSecretKey: string | null = $ref(null); +let turnstileSiteKey: string | null = $ref(null); +let turnstileSecretKey: string | null = $ref(null); async function init() { const meta = await os.api('admin/meta'); @@ -68,8 +85,10 @@ async function init() { hcaptchaSecretKey = meta.hcaptchaSecretKey; recaptchaSiteKey = meta.recaptchaSiteKey; recaptchaSecretKey = meta.recaptchaSecretKey; + turnstileSiteKey = meta.turnstileSiteKey; + turnstileSecretKey = meta.turnstileSecretKey; - provider = meta.enableHcaptcha ? 'hcaptcha' : meta.enableRecaptcha ? 'recaptcha' : null; + provider = meta.enableHcaptcha ? 'hcaptcha' : meta.enableRecaptcha ? 'recaptcha' : meta.enableTurnstile ? 'turnstile' : null; } function save() { @@ -80,6 +99,9 @@ function save() { enableRecaptcha: provider === 'recaptcha', recaptchaSiteKey, recaptchaSecretKey, + enableTurnstile: provider === 'turnstile', + turnstileSiteKey, + turnstileSecretKey, }).then(() => { fetchInstance(); }); diff --git a/packages/client/src/pages/admin/index.vue b/packages/client/src/pages/admin/index.vue index 9200b5d547..20f82bba28 100644 --- a/packages/client/src/pages/admin/index.vue +++ b/packages/client/src/pages/admin/index.vue @@ -53,7 +53,7 @@ let view = $ref(null); let el = $ref(null); let pageProps = $ref({}); let noMaintainerInformation = isEmpty(instance.maintainerName) || isEmpty(instance.maintainerEmail); -let noBotProtection = !instance.disableRegistration && !instance.enableHcaptcha && !instance.enableRecaptcha; +let noBotProtection = !instance.disableRegistration && !instance.enableHcaptcha && !instance.enableRecaptcha && !instance.enableTurnstile; let noEmailServer = !instance.enableEmail; let thereIsUnresolvedAbuseReport = $ref(false); let currentPage = $computed(() => router.currentRef.value.child); diff --git a/packages/client/src/pages/admin/security.vue b/packages/client/src/pages/admin/security.vue index c36cedb312..65d079c2cf 100644 --- a/packages/client/src/pages/admin/security.vue +++ b/packages/client/src/pages/admin/security.vue @@ -9,6 +9,7 @@ + @@ -120,6 +121,7 @@ import { definePageMetadata } from '@/scripts/page-metadata'; let summalyProxy: string = $ref(''); let enableHcaptcha: boolean = $ref(false); let enableRecaptcha: boolean = $ref(false); +let enableTurnstile: boolean = $ref(false); let sensitiveMediaDetection: string = $ref('none'); let sensitiveMediaDetectionSensitivity: number = $ref(0); let setSensitiveFlagAutomatically: boolean = $ref(false); @@ -132,6 +134,7 @@ async function init() { summalyProxy = meta.summalyProxy; enableHcaptcha = meta.enableHcaptcha; enableRecaptcha = meta.enableRecaptcha; + enableTurnstile = meta.enableTurnstile; sensitiveMediaDetection = meta.sensitiveMediaDetection; sensitiveMediaDetectionSensitivity = meta.sensitiveMediaDetectionSensitivity === 'veryLow' ? 0 : -- cgit v1.2.3-freya From 2492f4e81e9403d6c6efca28f0f65f4eed57ea75 Mon Sep 17 00:00:00 2001 From: squidicuz Date: Thu, 10 Nov 2022 21:22:31 -0500 Subject: fix for pinned users. update changelog (#9159) --- CHANGELOG.md | 1 + packages/backend/src/server/api/endpoints/pinned-users.ts | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) (limited to 'packages/backend/src/server/api') diff --git a/CHANGELOG.md b/CHANGELOG.md index 66959cd716..d7513acdb7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ You should also include the user name that made the change. ### Improvements ### Bugfixes +- Server: Bug fix for Pinned Users lookup on instance @squidicuzz ## 12.119.0 (2022/09/10) diff --git a/packages/backend/src/server/api/endpoints/pinned-users.ts b/packages/backend/src/server/api/endpoints/pinned-users.ts index 6c941314e2..f2c6e798ef 100644 --- a/packages/backend/src/server/api/endpoints/pinned-users.ts +++ b/packages/backend/src/server/api/endpoints/pinned-users.ts @@ -43,12 +43,12 @@ export default class extends Endpoint { super(meta, paramDef, async (ps, me) => { const meta = await this.metaService.fetch(); - const users = await Promise.all(meta.pinnedthis.usersRepository.map(acct => Acct.parse(acct)).map(acct => this.usersRepository.findOneBy({ + const users = await Promise.all(meta.pinnedUsers.map(acct => Acct.parse(acct)).map(acct => this.usersRepository.findOneBy({ usernameLower: acct.username.toLowerCase(), host: acct.host ?? IsNull(), }))); - return await this.userEntityService.packMany(users.filter(x => x !== undefined) as User[], me, { detail: true }); + return await this.userEntityService.packMany(users.filter(x => x !== null) as User[], me, { detail: true }); }); } } -- cgit v1.2.3-freya