diff --git a/CHANGELOG.md b/CHANGELOG.md index e02085e0a3..af2370f3ae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -54,7 +54,6 @@ - Fix: 絵文字ピッカーでバッテリーの絵文字が複数表示される問題を修正 #12197 ### Server -- Feat: Registry APIがサードパーティから利用可能になりました - Enhance: RedisへのTLのキャッシュ(FTT)をオフにできるように - Enhance: フォローしているチャンネルをフォロー解除した時(またはその逆)、タイムラインに反映される間隔を改善 - Enhance: プロフィールの自己紹介欄のMFMが連合するようになりました diff --git a/packages/backend/src/core/CoreModule.ts b/packages/backend/src/core/CoreModule.ts index 9fb29e0e68..c17ea9999a 100644 --- a/packages/backend/src/core/CoreModule.ts +++ b/packages/backend/src/core/CoreModule.ts @@ -64,7 +64,6 @@ import { ClipService } from './ClipService.js'; import { FeaturedService } from './FeaturedService.js'; import { FunoutTimelineService } from './FunoutTimelineService.js'; import { ChannelFollowingService } from './ChannelFollowingService.js'; -import { RegistryApiService } from './RegistryApiService.js'; import { ChartLoggerService } from './chart/ChartLoggerService.js'; import FederationChart from './chart/charts/federation.js'; import NotesChart from './chart/charts/notes.js'; @@ -196,7 +195,6 @@ const $ClipService: Provider = { provide: 'ClipService', useExisting: ClipServic const $FeaturedService: Provider = { provide: 'FeaturedService', useExisting: FeaturedService }; const $FunoutTimelineService: Provider = { provide: 'FunoutTimelineService', useExisting: FunoutTimelineService }; const $ChannelFollowingService: Provider = { provide: 'ChannelFollowingService', useExisting: ChannelFollowingService }; -const $RegistryApiService: Provider = { provide: 'RegistryApiService', useExisting: RegistryApiService }; const $ChartLoggerService: Provider = { provide: 'ChartLoggerService', useExisting: ChartLoggerService }; const $FederationChart: Provider = { provide: 'FederationChart', useExisting: FederationChart }; @@ -332,7 +330,6 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting FeaturedService, FunoutTimelineService, ChannelFollowingService, - RegistryApiService, ChartLoggerService, FederationChart, NotesChart, @@ -461,7 +458,6 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting $FeaturedService, $FunoutTimelineService, $ChannelFollowingService, - $RegistryApiService, $ChartLoggerService, $FederationChart, $NotesChart, @@ -591,7 +587,6 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting FeaturedService, FunoutTimelineService, ChannelFollowingService, - RegistryApiService, FederationChart, NotesChart, UsersChart, @@ -719,7 +714,6 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting $FeaturedService, $FunoutTimelineService, $ChannelFollowingService, - $RegistryApiService, $FederationChart, $NotesChart, $UsersChart, diff --git a/packages/backend/src/core/RegistryApiService.ts b/packages/backend/src/core/RegistryApiService.ts deleted file mode 100644 index d340c5e480..0000000000 --- a/packages/backend/src/core/RegistryApiService.ts +++ /dev/null @@ -1,147 +0,0 @@ -/* - * SPDX-FileCopyrightText: syuilo and other misskey contributors - * SPDX-License-Identifier: AGPL-3.0-only - */ - -import { Inject, Injectable } from '@nestjs/common'; -import { DI } from '@/di-symbols.js'; -import type { MiRegistryItem, RegistryItemsRepository } from '@/models/_.js'; -import { IdentifiableError } from '@/misc/identifiable-error.js'; -import type { MiUser } from '@/models/User.js'; -import { IdService } from '@/core/IdService.js'; -import { GlobalEventService } from '@/core/GlobalEventService.js'; -import { bindThis } from '@/decorators.js'; - -@Injectable() -export class RegistryApiService { - constructor( - @Inject(DI.registryItemsRepository) - private registryItemsRepository: RegistryItemsRepository, - - private idService: IdService, - private globalEventService: GlobalEventService, - ) { - } - - @bindThis - public async set(userId: MiUser['id'], domain: string | null, scope: string[], key: string, value: any) { - // TODO: 作成できるキーの数を制限する - - const query = this.registryItemsRepository.createQueryBuilder('item'); - if (domain) { - query.where('item.domain = :domain', { domain: domain }); - } else { - query.where('item.domain IS NULL'); - } - query.andWhere('item.userId = :userId', { userId: userId }); - query.andWhere('item.key = :key', { key: key }); - query.andWhere('item.scope = :scope', { scope: scope }); - - const existingItem = await query.getOne(); - - if (existingItem) { - await this.registryItemsRepository.update(existingItem.id, { - updatedAt: new Date(), - value: value, - }); - } else { - await this.registryItemsRepository.insert({ - id: this.idService.gen(), - updatedAt: new Date(), - userId: userId, - domain: domain, - scope: scope, - key: key, - value: value, - }); - } - - if (domain == null) { - // TODO: サードパーティアプリが傍受出来てしまうのでどうにかする - this.globalEventService.publishMainStream(userId, 'registryUpdated', { - scope: scope, - key: key, - value: value, - }); - } - } - - @bindThis - public async getItem(userId: MiUser['id'], domain: string | null, scope: string[], key: string): Promise { - const query = this.registryItemsRepository.createQueryBuilder('item') - .where(domain == null ? 'item.domain IS NULL' : 'item.domain = :domain', { domain: domain }) - .andWhere('item.userId = :userId', { userId: userId }) - .andWhere('item.key = :key', { key: key }) - .andWhere('item.scope = :scope', { scope: scope }); - - const item = await query.getOne(); - - return item; - } - - @bindThis - public async getAllItemsOfScope(userId: MiUser['id'], domain: string | null, scope: string[]): Promise { - const query = this.registryItemsRepository.createQueryBuilder('item'); - query.where(domain == null ? 'item.domain IS NULL' : 'item.domain = :domain', { domain: domain }); - query.andWhere('item.userId = :userId', { userId: userId }); - query.andWhere('item.scope = :scope', { scope: scope }); - - const items = await query.getMany(); - - return items; - } - - @bindThis - public async getAllKeysOfScope(userId: MiUser['id'], domain: string | null, scope: string[]): Promise { - const query = this.registryItemsRepository.createQueryBuilder('item'); - query.select('item.key'); - query.where(domain == null ? 'item.domain IS NULL' : 'item.domain = :domain', { domain: domain }); - query.andWhere('item.userId = :userId', { userId: userId }); - query.andWhere('item.scope = :scope', { scope: scope }); - - const items = await query.getMany(); - - return items.map(x => x.key); - } - - @bindThis - public async getAllScopeAndDomains(userId: MiUser['id']): Promise<{ domain: string | null; scopes: string[][] }[]> { - const query = this.registryItemsRepository.createQueryBuilder('item') - .select(['item.scope', 'item.domain']) - .where('item.userId = :userId', { userId: userId }); - - const items = await query.getMany(); - - const res = [] as { domain: string | null; scopes: string[][] }[]; - - for (const item of items) { - const target = res.find(x => x.domain === item.domain); - if (target) { - if (target.scopes.some(scope => scope.join('.') === item.scope.join('.'))) continue; - target.scopes.push(item.scope); - } else { - res.push({ - domain: item.domain, - scopes: [item.scope], - }); - } - } - - return res; - } - - @bindThis - public async remove(userId: MiUser['id'], domain: string | null, scope: string[], key: string) { - const query = this.registryItemsRepository.createQueryBuilder().delete(); - if (domain) { - query.where('domain = :domain', { domain: domain }); - } else { - query.where('domain IS NULL'); - } - query.andWhere('userId = :userId', { userId: userId }); - query.andWhere('key = :key', { key: key }); - query.andWhere('scope = :scope', { scope: scope }); - - await query.execute(); - } -} diff --git a/packages/backend/src/server/api/EndpointsModule.ts b/packages/backend/src/server/api/EndpointsModule.ts index 23067a9b26..3f8a46d855 100644 --- a/packages/backend/src/server/api/EndpointsModule.ts +++ b/packages/backend/src/server/api/EndpointsModule.ts @@ -230,7 +230,7 @@ import * as ep___i_registry_get from './endpoints/i/registry/get.js'; import * as ep___i_registry_keysWithType from './endpoints/i/registry/keys-with-type.js'; import * as ep___i_registry_keys from './endpoints/i/registry/keys.js'; import * as ep___i_registry_remove from './endpoints/i/registry/remove.js'; -import * as ep___i_registry_scopesWithDomain from './endpoints/i/registry/scopes-with-domain.js'; +import * as ep___i_registry_scopes from './endpoints/i/registry/scopes.js'; import * as ep___i_registry_set from './endpoints/i/registry/set.js'; import * as ep___i_revokeToken from './endpoints/i/revoke-token.js'; import * as ep___i_signinHistory from './endpoints/i/signin-history.js'; @@ -588,7 +588,7 @@ const $i_registry_get: Provider = { provide: 'ep:i/registry/get', useClass: ep__ const $i_registry_keysWithType: Provider = { provide: 'ep:i/registry/keys-with-type', useClass: ep___i_registry_keysWithType.default }; const $i_registry_keys: Provider = { provide: 'ep:i/registry/keys', useClass: ep___i_registry_keys.default }; const $i_registry_remove: Provider = { provide: 'ep:i/registry/remove', useClass: ep___i_registry_remove.default }; -const $i_registry_scopesWithDomain: Provider = { provide: 'ep:i/registry/scopes-with-domain', useClass: ep___i_registry_scopesWithDomain.default }; +const $i_registry_scopes: Provider = { provide: 'ep:i/registry/scopes', useClass: ep___i_registry_scopes.default }; const $i_registry_set: Provider = { provide: 'ep:i/registry/set', useClass: ep___i_registry_set.default }; const $i_revokeToken: Provider = { provide: 'ep:i/revoke-token', useClass: ep___i_revokeToken.default }; const $i_signinHistory: Provider = { provide: 'ep:i/signin-history', useClass: ep___i_signinHistory.default }; @@ -950,7 +950,7 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention $i_registry_keysWithType, $i_registry_keys, $i_registry_remove, - $i_registry_scopesWithDomain, + $i_registry_scopes, $i_registry_set, $i_revokeToken, $i_signinHistory, @@ -1306,7 +1306,7 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention $i_registry_keysWithType, $i_registry_keys, $i_registry_remove, - $i_registry_scopesWithDomain, + $i_registry_scopes, $i_registry_set, $i_revokeToken, $i_signinHistory, diff --git a/packages/backend/src/server/api/endpoints.ts b/packages/backend/src/server/api/endpoints.ts index af798fd166..e87e1df591 100644 --- a/packages/backend/src/server/api/endpoints.ts +++ b/packages/backend/src/server/api/endpoints.ts @@ -230,7 +230,7 @@ import * as ep___i_registry_get from './endpoints/i/registry/get.js'; import * as ep___i_registry_keysWithType from './endpoints/i/registry/keys-with-type.js'; import * as ep___i_registry_keys from './endpoints/i/registry/keys.js'; import * as ep___i_registry_remove from './endpoints/i/registry/remove.js'; -import * as ep___i_registry_scopesWithDomain from './endpoints/i/registry/scopes-with-domain.js'; +import * as ep___i_registry_scopes from './endpoints/i/registry/scopes.js'; import * as ep___i_registry_set from './endpoints/i/registry/set.js'; import * as ep___i_revokeToken from './endpoints/i/revoke-token.js'; import * as ep___i_signinHistory from './endpoints/i/signin-history.js'; @@ -586,7 +586,7 @@ const eps = [ ['i/registry/keys-with-type', ep___i_registry_keysWithType], ['i/registry/keys', ep___i_registry_keys], ['i/registry/remove', ep___i_registry_remove], - ['i/registry/scopes-with-domain', ep___i_registry_scopesWithDomain], + ['i/registry/scopes', ep___i_registry_scopes], ['i/registry/set', ep___i_registry_set], ['i/revoke-token', ep___i_revokeToken], ['i/signin-history', ep___i_signinHistory], diff --git a/packages/backend/src/server/api/endpoints/i/registry/get-all.ts b/packages/backend/src/server/api/endpoints/i/registry/get-all.ts index 29fa0a29cc..211e6637dc 100644 --- a/packages/backend/src/server/api/endpoints/i/registry/get-all.ts +++ b/packages/backend/src/server/api/endpoints/i/registry/get-all.ts @@ -5,10 +5,13 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { RegistryApiService } from '@/core/RegistryApiService.js'; +import type { RegistryItemsRepository } from '@/models/_.js'; +import { DI } from '@/di-symbols.js'; export const meta = { requireCredential: true, + + secure: true, } as const; export const paramDef = { @@ -17,18 +20,23 @@ export const paramDef = { scope: { type: 'array', default: [], items: { type: 'string', pattern: /^[a-zA-Z0-9_]+$/.toString().slice(1, -1), } }, - domain: { type: 'string', nullable: true }, }, - required: ['scope'], + required: [], } as const; @Injectable() export default class extends Endpoint { // eslint-disable-line import/no-default-export constructor( - private registryApiService: RegistryApiService, + @Inject(DI.registryItemsRepository) + private registryItemsRepository: RegistryItemsRepository, ) { - super(meta, paramDef, async (ps, me, accessToken) => { - const items = await this.registryApiService.getAllItemsOfScope(me.id, accessToken != null ? accessToken.id : (ps.domain ?? null), ps.scope); + super(meta, paramDef, async (ps, me) => { + const query = this.registryItemsRepository.createQueryBuilder('item') + .where('item.domain IS NULL') + .andWhere('item.userId = :userId', { userId: me.id }) + .andWhere('item.scope = :scope', { scope: ps.scope }); + + const items = await query.getMany(); const res = {} as Record; diff --git a/packages/backend/src/server/api/endpoints/i/registry/get-detail.ts b/packages/backend/src/server/api/endpoints/i/registry/get-detail.ts index 5b460b45d6..9c6f2d6781 100644 --- a/packages/backend/src/server/api/endpoints/i/registry/get-detail.ts +++ b/packages/backend/src/server/api/endpoints/i/registry/get-detail.ts @@ -5,12 +5,15 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { RegistryApiService } from '@/core/RegistryApiService.js'; +import type { RegistryItemsRepository } from '@/models/_.js'; +import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; export const meta = { requireCredential: true, + secure: true, + errors: { noSuchKey: { message: 'No such key.', @@ -27,18 +30,24 @@ export const paramDef = { scope: { type: 'array', default: [], items: { type: 'string', pattern: /^[a-zA-Z0-9_]+$/.toString().slice(1, -1), } }, - domain: { type: 'string', nullable: true }, }, - required: ['key', 'scope'], + required: ['key'], } as const; @Injectable() export default class extends Endpoint { // eslint-disable-line import/no-default-export constructor( - private registryApiService: RegistryApiService, + @Inject(DI.registryItemsRepository) + private registryItemsRepository: RegistryItemsRepository, ) { - super(meta, paramDef, async (ps, me, accessToken) => { - const item = await this.registryApiService.getItem(me.id, accessToken != null ? accessToken.id : (ps.domain ?? null), ps.scope, ps.key); + super(meta, paramDef, async (ps, me) => { + const query = this.registryItemsRepository.createQueryBuilder('item') + .where('item.domain IS NULL') + .andWhere('item.userId = :userId', { userId: me.id }) + .andWhere('item.key = :key', { key: ps.key }) + .andWhere('item.scope = :scope', { scope: ps.scope }); + + const item = await query.getOne(); if (item == null) { throw new ApiError(meta.errors.noSuchKey); diff --git a/packages/backend/src/server/api/endpoints/i/registry/get.ts b/packages/backend/src/server/api/endpoints/i/registry/get.ts index e8c28298ef..729e729b8c 100644 --- a/packages/backend/src/server/api/endpoints/i/registry/get.ts +++ b/packages/backend/src/server/api/endpoints/i/registry/get.ts @@ -5,12 +5,15 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { RegistryApiService } from '@/core/RegistryApiService.js'; +import type { RegistryItemsRepository } from '@/models/_.js'; +import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; export const meta = { requireCredential: true, + secure: true, + errors: { noSuchKey: { message: 'No such key.', @@ -27,18 +30,24 @@ export const paramDef = { scope: { type: 'array', default: [], items: { type: 'string', pattern: /^[a-zA-Z0-9_]+$/.toString().slice(1, -1), } }, - domain: { type: 'string', nullable: true }, }, - required: ['key', 'scope'], + required: ['key'], } as const; @Injectable() export default class extends Endpoint { // eslint-disable-line import/no-default-export constructor( - private registryApiService: RegistryApiService, + @Inject(DI.registryItemsRepository) + private registryItemsRepository: RegistryItemsRepository, ) { - super(meta, paramDef, async (ps, me, accessToken) => { - const item = await this.registryApiService.getItem(me.id, accessToken != null ? accessToken.id : (ps.domain ?? null), ps.scope, ps.key); + super(meta, paramDef, async (ps, me) => { + const query = this.registryItemsRepository.createQueryBuilder('item') + .where('item.domain IS NULL') + .andWhere('item.userId = :userId', { userId: me.id }) + .andWhere('item.key = :key', { key: ps.key }) + .andWhere('item.scope = :scope', { scope: ps.scope }); + + const item = await query.getOne(); if (item == null) { throw new ApiError(meta.errors.noSuchKey); diff --git a/packages/backend/src/server/api/endpoints/i/registry/keys-with-type.ts b/packages/backend/src/server/api/endpoints/i/registry/keys-with-type.ts index 8953ee5d3d..ffd2860fde 100644 --- a/packages/backend/src/server/api/endpoints/i/registry/keys-with-type.ts +++ b/packages/backend/src/server/api/endpoints/i/registry/keys-with-type.ts @@ -5,10 +5,13 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { RegistryApiService } from '@/core/RegistryApiService.js'; +import type { RegistryItemsRepository } from '@/models/_.js'; +import { DI } from '@/di-symbols.js'; export const meta = { requireCredential: true, + + secure: true, } as const; export const paramDef = { @@ -17,31 +20,36 @@ export const paramDef = { scope: { type: 'array', default: [], items: { type: 'string', pattern: /^[a-zA-Z0-9_]+$/.toString().slice(1, -1), } }, - domain: { type: 'string', nullable: true }, }, - required: ['scope'], + required: [], } as const; @Injectable() export default class extends Endpoint { // eslint-disable-line import/no-default-export constructor( - private registryApiService: RegistryApiService, + @Inject(DI.registryItemsRepository) + private registryItemsRepository: RegistryItemsRepository, ) { - super(meta, paramDef, async (ps, me, accessToken) => { - const items = await this.registryApiService.getAllItemsOfScope(me.id, accessToken != null ? accessToken.id : (ps.domain ?? null), ps.scope); + super(meta, paramDef, async (ps, me) => { + const query = this.registryItemsRepository.createQueryBuilder('item') + .where('item.domain IS NULL') + .andWhere('item.userId = :userId', { userId: me.id }) + .andWhere('item.scope = :scope', { scope: ps.scope }); + + const items = await query.getMany(); const res = {} as Record; for (const item of items) { const type = typeof item.value; res[item.key] = - item.value === null ? 'null' : - Array.isArray(item.value) ? 'array' : - type === 'number' ? 'number' : - type === 'string' ? 'string' : - type === 'boolean' ? 'boolean' : - type === 'object' ? 'object' : - null as never; + item.value === null ? 'null' : + Array.isArray(item.value) ? 'array' : + type === 'number' ? 'number' : + type === 'string' ? 'string' : + type === 'boolean' ? 'boolean' : + type === 'object' ? 'object' : + null as never; } return res; diff --git a/packages/backend/src/server/api/endpoints/i/registry/keys.ts b/packages/backend/src/server/api/endpoints/i/registry/keys.ts index 04e120d752..7239bb66e1 100644 --- a/packages/backend/src/server/api/endpoints/i/registry/keys.ts +++ b/packages/backend/src/server/api/endpoints/i/registry/keys.ts @@ -5,10 +5,13 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { RegistryApiService } from '@/core/RegistryApiService.js'; +import type { RegistryItemsRepository } from '@/models/_.js'; +import { DI } from '@/di-symbols.js'; export const meta = { requireCredential: true, + + secure: true, } as const; export const paramDef = { @@ -17,18 +20,26 @@ export const paramDef = { scope: { type: 'array', default: [], items: { type: 'string', pattern: /^[a-zA-Z0-9_]+$/.toString().slice(1, -1), } }, - domain: { type: 'string', nullable: true }, }, - required: ['scope'], + required: [], } as const; @Injectable() export default class extends Endpoint { // eslint-disable-line import/no-default-export constructor( - private registryApiService: RegistryApiService, + @Inject(DI.registryItemsRepository) + private registryItemsRepository: RegistryItemsRepository, ) { - super(meta, paramDef, async (ps, me, accessToken) => { - return await this.registryApiService.getAllKeysOfScope(me.id, accessToken != null ? accessToken.id : (ps.domain ?? null), ps.scope); + super(meta, paramDef, async (ps, me) => { + const query = this.registryItemsRepository.createQueryBuilder('item') + .select('item.key') + .where('item.domain IS NULL') + .andWhere('item.userId = :userId', { userId: me.id }) + .andWhere('item.scope = :scope', { scope: ps.scope }); + + const items = await query.getMany(); + + return items.map(x => x.key); }); } } diff --git a/packages/backend/src/server/api/endpoints/i/registry/remove.ts b/packages/backend/src/server/api/endpoints/i/registry/remove.ts index ba8100b547..ae687fefe9 100644 --- a/packages/backend/src/server/api/endpoints/i/registry/remove.ts +++ b/packages/backend/src/server/api/endpoints/i/registry/remove.ts @@ -7,12 +7,13 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { RegistryItemsRepository } from '@/models/_.js'; import { DI } from '@/di-symbols.js'; -import { RegistryApiService } from '@/core/RegistryApiService.js'; import { ApiError } from '../../../error.js'; export const meta = { requireCredential: true, + secure: true, + errors: { noSuchKey: { message: 'No such key.', @@ -29,18 +30,30 @@ export const paramDef = { scope: { type: 'array', default: [], items: { type: 'string', pattern: /^[a-zA-Z0-9_]+$/.toString().slice(1, -1), } }, - domain: { type: 'string', nullable: true }, }, - required: ['key', 'scope'], + required: ['key'], } as const; @Injectable() export default class extends Endpoint { // eslint-disable-line import/no-default-export constructor( - private registryApiService: RegistryApiService, + @Inject(DI.registryItemsRepository) + private registryItemsRepository: RegistryItemsRepository, ) { - super(meta, paramDef, async (ps, me, accessToken) => { - await this.registryApiService.remove(me.id, accessToken != null ? accessToken.id : (ps.domain ?? null), ps.scope, ps.key); + super(meta, paramDef, async (ps, me) => { + const query = this.registryItemsRepository.createQueryBuilder('item') + .where('item.domain IS NULL') + .andWhere('item.userId = :userId', { userId: me.id }) + .andWhere('item.key = :key', { key: ps.key }) + .andWhere('item.scope = :scope', { scope: ps.scope }); + + const item = await query.getOne(); + + if (item == null) { + throw new ApiError(meta.errors.noSuchKey); + } + + await this.registryItemsRepository.remove(item); }); } } diff --git a/packages/backend/src/server/api/endpoints/i/registry/scopes-with-domain.ts b/packages/backend/src/server/api/endpoints/i/registry/scopes-with-domain.ts deleted file mode 100644 index 1ff994b82c..0000000000 --- a/packages/backend/src/server/api/endpoints/i/registry/scopes-with-domain.ts +++ /dev/null @@ -1,30 +0,0 @@ -/* - * SPDX-FileCopyrightText: syuilo and other misskey contributors - * SPDX-License-Identifier: AGPL-3.0-only - */ - -import { Inject, Injectable } from '@nestjs/common'; -import { Endpoint } from '@/server/api/endpoint-base.js'; -import { RegistryApiService } from '@/core/RegistryApiService.js'; - -export const meta = { - requireCredential: true, - secure: true, -} as const; - -export const paramDef = { - type: 'object', - properties: {}, - required: [], -} as const; - -@Injectable() -export default class extends Endpoint { // eslint-disable-line import/no-default-export - constructor( - private registryApiService: RegistryApiService, - ) { - super(meta, paramDef, async (ps, me) => { - return await this.registryApiService.getAllScopeAndDomains(me.id); - }); - } -} diff --git a/packages/backend/src/server/api/endpoints/i/registry/scopes.ts b/packages/backend/src/server/api/endpoints/i/registry/scopes.ts new file mode 100644 index 0000000000..7637cdcf73 --- /dev/null +++ b/packages/backend/src/server/api/endpoints/i/registry/scopes.ts @@ -0,0 +1,47 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import type { RegistryItemsRepository } from '@/models/_.js'; +import { DI } from '@/di-symbols.js'; + +export const meta = { + requireCredential: true, + + secure: true, +} as const; + +export const paramDef = { + type: 'object', + properties: {}, + required: [], +} as const; + +@Injectable() +export default class extends Endpoint { // eslint-disable-line import/no-default-export + constructor( + @Inject(DI.registryItemsRepository) + private registryItemsRepository: RegistryItemsRepository, + ) { + super(meta, paramDef, async (ps, me) => { + const query = this.registryItemsRepository.createQueryBuilder('item') + .select('item.scope') + .where('item.domain IS NULL') + .andWhere('item.userId = :userId', { userId: me.id }); + + const items = await query.getMany(); + + const res = [] as string[][]; + + for (const item of items) { + if (res.some(scope => scope.join('.') === item.scope.join('.'))) continue; + res.push(item.scope); + } + + return res; + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/i/registry/set.ts b/packages/backend/src/server/api/endpoints/i/registry/set.ts index 58bb450bce..6203e7aa8b 100644 --- a/packages/backend/src/server/api/endpoints/i/registry/set.ts +++ b/packages/backend/src/server/api/endpoints/i/registry/set.ts @@ -5,10 +5,15 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { RegistryApiService } from '@/core/RegistryApiService.js'; +import type { RegistryItemsRepository } from '@/models/_.js'; +import { IdService } from '@/core/IdService.js'; +import { GlobalEventService } from '@/core/GlobalEventService.js'; +import { DI } from '@/di-symbols.js'; export const meta = { requireCredential: true, + + secure: true, } as const; export const paramDef = { @@ -19,18 +24,51 @@ export const paramDef = { scope: { type: 'array', default: [], items: { type: 'string', pattern: /^[a-zA-Z0-9_]+$/.toString().slice(1, -1), } }, - domain: { type: 'string', nullable: true }, }, - required: ['key', 'value', 'scope'], + required: ['key', 'value'], } as const; @Injectable() export default class extends Endpoint { // eslint-disable-line import/no-default-export constructor( - private registryApiService: RegistryApiService, + @Inject(DI.registryItemsRepository) + private registryItemsRepository: RegistryItemsRepository, + + private idService: IdService, + private globalEventService: GlobalEventService, ) { - super(meta, paramDef, async (ps, me, accessToken) => { - await this.registryApiService.set(me.id, accessToken ? accessToken.id : (ps.domain ?? null), ps.scope, ps.key, ps.value); + super(meta, paramDef, async (ps, me) => { + const query = this.registryItemsRepository.createQueryBuilder('item') + .where('item.domain IS NULL') + .andWhere('item.userId = :userId', { userId: me.id }) + .andWhere('item.key = :key', { key: ps.key }) + .andWhere('item.scope = :scope', { scope: ps.scope }); + + const existingItem = await query.getOne(); + + if (existingItem) { + await this.registryItemsRepository.update(existingItem.id, { + updatedAt: new Date(), + value: ps.value, + }); + } else { + await this.registryItemsRepository.insert({ + id: this.idService.gen(), + updatedAt: new Date(), + userId: me.id, + domain: null, + scope: ps.scope, + key: ps.key, + value: ps.value, + }); + } + + // TODO: サードパーティアプリが傍受出来てしまうのでどうにかする + this.globalEventService.publishMainStream(me.id, 'registryUpdated', { + scope: ps.scope, + key: ps.key, + value: ps.value, + }); }); } } diff --git a/packages/frontend/src/pages/about-misskey.vue b/packages/frontend/src/pages/about-misskey.vue index b446a4d554..7a2c698d11 100644 --- a/packages/frontend/src/pages/about-misskey.vue +++ b/packages/frontend/src/pages/about-misskey.vue @@ -28,7 +28,7 @@ SPDX-License-Identifier: AGPL-3.0-only I #Misskey -
+