feat: レジストリAPIをサードパーティから利用可能に (#12229)
* wip * wip * Update remove.ts * refactor
This commit is contained in:
parent
82526ad4f3
commit
79346272f8
|
@ -53,6 +53,7 @@
|
||||||
- Fix: 絵文字ピッカーでバッテリーの絵文字が複数表示される問題を修正 #12197
|
- Fix: 絵文字ピッカーでバッテリーの絵文字が複数表示される問題を修正 #12197
|
||||||
|
|
||||||
### Server
|
### Server
|
||||||
|
- Feat: Registry APIがサードパーティから利用可能になりました
|
||||||
- Enhance: RedisへのTLのキャッシュ(FTT)をオフにできるように
|
- Enhance: RedisへのTLのキャッシュ(FTT)をオフにできるように
|
||||||
- Enhance: フォローしているチャンネルをフォロー解除した時(またはその逆)、タイムラインに反映される間隔を改善
|
- Enhance: フォローしているチャンネルをフォロー解除した時(またはその逆)、タイムラインに反映される間隔を改善
|
||||||
- Enhance: プロフィールの自己紹介欄のMFMが連合するようになりました
|
- Enhance: プロフィールの自己紹介欄のMFMが連合するようになりました
|
||||||
|
|
|
@ -64,6 +64,7 @@ import { ClipService } from './ClipService.js';
|
||||||
import { FeaturedService } from './FeaturedService.js';
|
import { FeaturedService } from './FeaturedService.js';
|
||||||
import { FunoutTimelineService } from './FunoutTimelineService.js';
|
import { FunoutTimelineService } from './FunoutTimelineService.js';
|
||||||
import { ChannelFollowingService } from './ChannelFollowingService.js';
|
import { ChannelFollowingService } from './ChannelFollowingService.js';
|
||||||
|
import { RegistryApiService } from './RegistryApiService.js';
|
||||||
import { ChartLoggerService } from './chart/ChartLoggerService.js';
|
import { ChartLoggerService } from './chart/ChartLoggerService.js';
|
||||||
import FederationChart from './chart/charts/federation.js';
|
import FederationChart from './chart/charts/federation.js';
|
||||||
import NotesChart from './chart/charts/notes.js';
|
import NotesChart from './chart/charts/notes.js';
|
||||||
|
@ -195,6 +196,7 @@ const $ClipService: Provider = { provide: 'ClipService', useExisting: ClipServic
|
||||||
const $FeaturedService: Provider = { provide: 'FeaturedService', useExisting: FeaturedService };
|
const $FeaturedService: Provider = { provide: 'FeaturedService', useExisting: FeaturedService };
|
||||||
const $FunoutTimelineService: Provider = { provide: 'FunoutTimelineService', useExisting: FunoutTimelineService };
|
const $FunoutTimelineService: Provider = { provide: 'FunoutTimelineService', useExisting: FunoutTimelineService };
|
||||||
const $ChannelFollowingService: Provider = { provide: 'ChannelFollowingService', useExisting: ChannelFollowingService };
|
const $ChannelFollowingService: Provider = { provide: 'ChannelFollowingService', useExisting: ChannelFollowingService };
|
||||||
|
const $RegistryApiService: Provider = { provide: 'RegistryApiService', useExisting: RegistryApiService };
|
||||||
|
|
||||||
const $ChartLoggerService: Provider = { provide: 'ChartLoggerService', useExisting: ChartLoggerService };
|
const $ChartLoggerService: Provider = { provide: 'ChartLoggerService', useExisting: ChartLoggerService };
|
||||||
const $FederationChart: Provider = { provide: 'FederationChart', useExisting: FederationChart };
|
const $FederationChart: Provider = { provide: 'FederationChart', useExisting: FederationChart };
|
||||||
|
@ -330,6 +332,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
|
||||||
FeaturedService,
|
FeaturedService,
|
||||||
FunoutTimelineService,
|
FunoutTimelineService,
|
||||||
ChannelFollowingService,
|
ChannelFollowingService,
|
||||||
|
RegistryApiService,
|
||||||
ChartLoggerService,
|
ChartLoggerService,
|
||||||
FederationChart,
|
FederationChart,
|
||||||
NotesChart,
|
NotesChart,
|
||||||
|
@ -458,6 +461,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
|
||||||
$FeaturedService,
|
$FeaturedService,
|
||||||
$FunoutTimelineService,
|
$FunoutTimelineService,
|
||||||
$ChannelFollowingService,
|
$ChannelFollowingService,
|
||||||
|
$RegistryApiService,
|
||||||
$ChartLoggerService,
|
$ChartLoggerService,
|
||||||
$FederationChart,
|
$FederationChart,
|
||||||
$NotesChart,
|
$NotesChart,
|
||||||
|
@ -587,6 +591,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
|
||||||
FeaturedService,
|
FeaturedService,
|
||||||
FunoutTimelineService,
|
FunoutTimelineService,
|
||||||
ChannelFollowingService,
|
ChannelFollowingService,
|
||||||
|
RegistryApiService,
|
||||||
FederationChart,
|
FederationChart,
|
||||||
NotesChart,
|
NotesChart,
|
||||||
UsersChart,
|
UsersChart,
|
||||||
|
@ -714,6 +719,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
|
||||||
$FeaturedService,
|
$FeaturedService,
|
||||||
$FunoutTimelineService,
|
$FunoutTimelineService,
|
||||||
$ChannelFollowingService,
|
$ChannelFollowingService,
|
||||||
|
$RegistryApiService,
|
||||||
$FederationChart,
|
$FederationChart,
|
||||||
$NotesChart,
|
$NotesChart,
|
||||||
$UsersChart,
|
$UsersChart,
|
||||||
|
|
147
packages/backend/src/core/RegistryApiService.ts
Normal file
147
packages/backend/src/core/RegistryApiService.ts
Normal file
|
@ -0,0 +1,147 @@
|
||||||
|
/*
|
||||||
|
* 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<MiRegistryItem | null> {
|
||||||
|
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<MiRegistryItem[]> {
|
||||||
|
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<string[]> {
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
|
@ -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_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_keys from './endpoints/i/registry/keys.js';
|
||||||
import * as ep___i_registry_remove from './endpoints/i/registry/remove.js';
|
import * as ep___i_registry_remove from './endpoints/i/registry/remove.js';
|
||||||
import * as ep___i_registry_scopes from './endpoints/i/registry/scopes.js';
|
import * as ep___i_registry_scopesWithDomain from './endpoints/i/registry/scopes-with-domain.js';
|
||||||
import * as ep___i_registry_set from './endpoints/i/registry/set.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_revokeToken from './endpoints/i/revoke-token.js';
|
||||||
import * as ep___i_signinHistory from './endpoints/i/signin-history.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_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_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_remove: Provider = { provide: 'ep:i/registry/remove', useClass: ep___i_registry_remove.default };
|
||||||
const $i_registry_scopes: Provider = { provide: 'ep:i/registry/scopes', useClass: ep___i_registry_scopes.default };
|
const $i_registry_scopesWithDomain: Provider = { provide: 'ep:i/registry/scopes-with-domain', useClass: ep___i_registry_scopesWithDomain.default };
|
||||||
const $i_registry_set: Provider = { provide: 'ep:i/registry/set', useClass: ep___i_registry_set.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_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 };
|
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_keysWithType,
|
||||||
$i_registry_keys,
|
$i_registry_keys,
|
||||||
$i_registry_remove,
|
$i_registry_remove,
|
||||||
$i_registry_scopes,
|
$i_registry_scopesWithDomain,
|
||||||
$i_registry_set,
|
$i_registry_set,
|
||||||
$i_revokeToken,
|
$i_revokeToken,
|
||||||
$i_signinHistory,
|
$i_signinHistory,
|
||||||
|
@ -1306,7 +1306,7 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention
|
||||||
$i_registry_keysWithType,
|
$i_registry_keysWithType,
|
||||||
$i_registry_keys,
|
$i_registry_keys,
|
||||||
$i_registry_remove,
|
$i_registry_remove,
|
||||||
$i_registry_scopes,
|
$i_registry_scopesWithDomain,
|
||||||
$i_registry_set,
|
$i_registry_set,
|
||||||
$i_revokeToken,
|
$i_revokeToken,
|
||||||
$i_signinHistory,
|
$i_signinHistory,
|
||||||
|
|
|
@ -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_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_keys from './endpoints/i/registry/keys.js';
|
||||||
import * as ep___i_registry_remove from './endpoints/i/registry/remove.js';
|
import * as ep___i_registry_remove from './endpoints/i/registry/remove.js';
|
||||||
import * as ep___i_registry_scopes from './endpoints/i/registry/scopes.js';
|
import * as ep___i_registry_scopesWithDomain from './endpoints/i/registry/scopes-with-domain.js';
|
||||||
import * as ep___i_registry_set from './endpoints/i/registry/set.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_revokeToken from './endpoints/i/revoke-token.js';
|
||||||
import * as ep___i_signinHistory from './endpoints/i/signin-history.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-with-type', ep___i_registry_keysWithType],
|
||||||
['i/registry/keys', ep___i_registry_keys],
|
['i/registry/keys', ep___i_registry_keys],
|
||||||
['i/registry/remove', ep___i_registry_remove],
|
['i/registry/remove', ep___i_registry_remove],
|
||||||
['i/registry/scopes', ep___i_registry_scopes],
|
['i/registry/scopes-with-domain', ep___i_registry_scopesWithDomain],
|
||||||
['i/registry/set', ep___i_registry_set],
|
['i/registry/set', ep___i_registry_set],
|
||||||
['i/revoke-token', ep___i_revokeToken],
|
['i/revoke-token', ep___i_revokeToken],
|
||||||
['i/signin-history', ep___i_signinHistory],
|
['i/signin-history', ep___i_signinHistory],
|
||||||
|
|
|
@ -5,13 +5,10 @@
|
||||||
|
|
||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
import { Endpoint } from '@/server/api/endpoint-base.js';
|
import { Endpoint } from '@/server/api/endpoint-base.js';
|
||||||
import type { RegistryItemsRepository } from '@/models/_.js';
|
import { RegistryApiService } from '@/core/RegistryApiService.js';
|
||||||
import { DI } from '@/di-symbols.js';
|
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
requireCredential: true,
|
requireCredential: true,
|
||||||
|
|
||||||
secure: true,
|
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const paramDef = {
|
export const paramDef = {
|
||||||
|
@ -20,23 +17,18 @@ export const paramDef = {
|
||||||
scope: { type: 'array', default: [], items: {
|
scope: { type: 'array', default: [], items: {
|
||||||
type: 'string', pattern: /^[a-zA-Z0-9_]+$/.toString().slice(1, -1),
|
type: 'string', pattern: /^[a-zA-Z0-9_]+$/.toString().slice(1, -1),
|
||||||
} },
|
} },
|
||||||
|
domain: { type: 'string', nullable: true },
|
||||||
},
|
},
|
||||||
required: [],
|
required: ['scope'],
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
|
export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(DI.registryItemsRepository)
|
private registryApiService: RegistryApiService,
|
||||||
private registryItemsRepository: RegistryItemsRepository,
|
|
||||||
) {
|
) {
|
||||||
super(meta, paramDef, async (ps, me) => {
|
super(meta, paramDef, async (ps, me, accessToken) => {
|
||||||
const query = this.registryItemsRepository.createQueryBuilder('item')
|
const items = await this.registryApiService.getAllItemsOfScope(me.id, accessToken != null ? accessToken.id : (ps.domain ?? null), ps.scope);
|
||||||
.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<string, any>;
|
const res = {} as Record<string, any>;
|
||||||
|
|
||||||
|
|
|
@ -5,15 +5,12 @@
|
||||||
|
|
||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
import { Endpoint } from '@/server/api/endpoint-base.js';
|
import { Endpoint } from '@/server/api/endpoint-base.js';
|
||||||
import type { RegistryItemsRepository } from '@/models/_.js';
|
import { RegistryApiService } from '@/core/RegistryApiService.js';
|
||||||
import { DI } from '@/di-symbols.js';
|
|
||||||
import { ApiError } from '../../../error.js';
|
import { ApiError } from '../../../error.js';
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
requireCredential: true,
|
requireCredential: true,
|
||||||
|
|
||||||
secure: true,
|
|
||||||
|
|
||||||
errors: {
|
errors: {
|
||||||
noSuchKey: {
|
noSuchKey: {
|
||||||
message: 'No such key.',
|
message: 'No such key.',
|
||||||
|
@ -30,24 +27,18 @@ export const paramDef = {
|
||||||
scope: { type: 'array', default: [], items: {
|
scope: { type: 'array', default: [], items: {
|
||||||
type: 'string', pattern: /^[a-zA-Z0-9_]+$/.toString().slice(1, -1),
|
type: 'string', pattern: /^[a-zA-Z0-9_]+$/.toString().slice(1, -1),
|
||||||
} },
|
} },
|
||||||
|
domain: { type: 'string', nullable: true },
|
||||||
},
|
},
|
||||||
required: ['key'],
|
required: ['key', 'scope'],
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
|
export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(DI.registryItemsRepository)
|
private registryApiService: RegistryApiService,
|
||||||
private registryItemsRepository: RegistryItemsRepository,
|
|
||||||
) {
|
) {
|
||||||
super(meta, paramDef, async (ps, me) => {
|
super(meta, paramDef, async (ps, me, accessToken) => {
|
||||||
const query = this.registryItemsRepository.createQueryBuilder('item')
|
const item = await this.registryApiService.getItem(me.id, accessToken != null ? accessToken.id : (ps.domain ?? null), ps.scope, ps.key);
|
||||||
.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) {
|
if (item == null) {
|
||||||
throw new ApiError(meta.errors.noSuchKey);
|
throw new ApiError(meta.errors.noSuchKey);
|
||||||
|
|
|
@ -5,15 +5,12 @@
|
||||||
|
|
||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
import { Endpoint } from '@/server/api/endpoint-base.js';
|
import { Endpoint } from '@/server/api/endpoint-base.js';
|
||||||
import type { RegistryItemsRepository } from '@/models/_.js';
|
import { RegistryApiService } from '@/core/RegistryApiService.js';
|
||||||
import { DI } from '@/di-symbols.js';
|
|
||||||
import { ApiError } from '../../../error.js';
|
import { ApiError } from '../../../error.js';
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
requireCredential: true,
|
requireCredential: true,
|
||||||
|
|
||||||
secure: true,
|
|
||||||
|
|
||||||
errors: {
|
errors: {
|
||||||
noSuchKey: {
|
noSuchKey: {
|
||||||
message: 'No such key.',
|
message: 'No such key.',
|
||||||
|
@ -30,24 +27,18 @@ export const paramDef = {
|
||||||
scope: { type: 'array', default: [], items: {
|
scope: { type: 'array', default: [], items: {
|
||||||
type: 'string', pattern: /^[a-zA-Z0-9_]+$/.toString().slice(1, -1),
|
type: 'string', pattern: /^[a-zA-Z0-9_]+$/.toString().slice(1, -1),
|
||||||
} },
|
} },
|
||||||
|
domain: { type: 'string', nullable: true },
|
||||||
},
|
},
|
||||||
required: ['key'],
|
required: ['key', 'scope'],
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
|
export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(DI.registryItemsRepository)
|
private registryApiService: RegistryApiService,
|
||||||
private registryItemsRepository: RegistryItemsRepository,
|
|
||||||
) {
|
) {
|
||||||
super(meta, paramDef, async (ps, me) => {
|
super(meta, paramDef, async (ps, me, accessToken) => {
|
||||||
const query = this.registryItemsRepository.createQueryBuilder('item')
|
const item = await this.registryApiService.getItem(me.id, accessToken != null ? accessToken.id : (ps.domain ?? null), ps.scope, ps.key);
|
||||||
.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) {
|
if (item == null) {
|
||||||
throw new ApiError(meta.errors.noSuchKey);
|
throw new ApiError(meta.errors.noSuchKey);
|
||||||
|
|
|
@ -5,13 +5,10 @@
|
||||||
|
|
||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
import { Endpoint } from '@/server/api/endpoint-base.js';
|
import { Endpoint } from '@/server/api/endpoint-base.js';
|
||||||
import type { RegistryItemsRepository } from '@/models/_.js';
|
import { RegistryApiService } from '@/core/RegistryApiService.js';
|
||||||
import { DI } from '@/di-symbols.js';
|
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
requireCredential: true,
|
requireCredential: true,
|
||||||
|
|
||||||
secure: true,
|
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const paramDef = {
|
export const paramDef = {
|
||||||
|
@ -20,23 +17,18 @@ export const paramDef = {
|
||||||
scope: { type: 'array', default: [], items: {
|
scope: { type: 'array', default: [], items: {
|
||||||
type: 'string', pattern: /^[a-zA-Z0-9_]+$/.toString().slice(1, -1),
|
type: 'string', pattern: /^[a-zA-Z0-9_]+$/.toString().slice(1, -1),
|
||||||
} },
|
} },
|
||||||
|
domain: { type: 'string', nullable: true },
|
||||||
},
|
},
|
||||||
required: [],
|
required: ['scope'],
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
|
export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(DI.registryItemsRepository)
|
private registryApiService: RegistryApiService,
|
||||||
private registryItemsRepository: RegistryItemsRepository,
|
|
||||||
) {
|
) {
|
||||||
super(meta, paramDef, async (ps, me) => {
|
super(meta, paramDef, async (ps, me, accessToken) => {
|
||||||
const query = this.registryItemsRepository.createQueryBuilder('item')
|
const items = await this.registryApiService.getAllItemsOfScope(me.id, accessToken != null ? accessToken.id : (ps.domain ?? null), ps.scope);
|
||||||
.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<string, string>;
|
const res = {} as Record<string, string>;
|
||||||
|
|
||||||
|
|
|
@ -5,13 +5,10 @@
|
||||||
|
|
||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
import { Endpoint } from '@/server/api/endpoint-base.js';
|
import { Endpoint } from '@/server/api/endpoint-base.js';
|
||||||
import type { RegistryItemsRepository } from '@/models/_.js';
|
import { RegistryApiService } from '@/core/RegistryApiService.js';
|
||||||
import { DI } from '@/di-symbols.js';
|
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
requireCredential: true,
|
requireCredential: true,
|
||||||
|
|
||||||
secure: true,
|
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const paramDef = {
|
export const paramDef = {
|
||||||
|
@ -20,26 +17,18 @@ export const paramDef = {
|
||||||
scope: { type: 'array', default: [], items: {
|
scope: { type: 'array', default: [], items: {
|
||||||
type: 'string', pattern: /^[a-zA-Z0-9_]+$/.toString().slice(1, -1),
|
type: 'string', pattern: /^[a-zA-Z0-9_]+$/.toString().slice(1, -1),
|
||||||
} },
|
} },
|
||||||
|
domain: { type: 'string', nullable: true },
|
||||||
},
|
},
|
||||||
required: [],
|
required: ['scope'],
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
|
export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(DI.registryItemsRepository)
|
private registryApiService: RegistryApiService,
|
||||||
private registryItemsRepository: RegistryItemsRepository,
|
|
||||||
) {
|
) {
|
||||||
super(meta, paramDef, async (ps, me) => {
|
super(meta, paramDef, async (ps, me, accessToken) => {
|
||||||
const query = this.registryItemsRepository.createQueryBuilder('item')
|
return await this.registryApiService.getAllKeysOfScope(me.id, accessToken != null ? accessToken.id : (ps.domain ?? null), ps.scope);
|
||||||
.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);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,13 +7,12 @@ import { Inject, Injectable } from '@nestjs/common';
|
||||||
import { Endpoint } from '@/server/api/endpoint-base.js';
|
import { Endpoint } from '@/server/api/endpoint-base.js';
|
||||||
import type { RegistryItemsRepository } from '@/models/_.js';
|
import type { RegistryItemsRepository } from '@/models/_.js';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
|
import { RegistryApiService } from '@/core/RegistryApiService.js';
|
||||||
import { ApiError } from '../../../error.js';
|
import { ApiError } from '../../../error.js';
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
requireCredential: true,
|
requireCredential: true,
|
||||||
|
|
||||||
secure: true,
|
|
||||||
|
|
||||||
errors: {
|
errors: {
|
||||||
noSuchKey: {
|
noSuchKey: {
|
||||||
message: 'No such key.',
|
message: 'No such key.',
|
||||||
|
@ -30,30 +29,18 @@ export const paramDef = {
|
||||||
scope: { type: 'array', default: [], items: {
|
scope: { type: 'array', default: [], items: {
|
||||||
type: 'string', pattern: /^[a-zA-Z0-9_]+$/.toString().slice(1, -1),
|
type: 'string', pattern: /^[a-zA-Z0-9_]+$/.toString().slice(1, -1),
|
||||||
} },
|
} },
|
||||||
|
domain: { type: 'string', nullable: true },
|
||||||
},
|
},
|
||||||
required: ['key'],
|
required: ['key', 'scope'],
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
|
export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(DI.registryItemsRepository)
|
private registryApiService: RegistryApiService,
|
||||||
private registryItemsRepository: RegistryItemsRepository,
|
|
||||||
) {
|
) {
|
||||||
super(meta, paramDef, async (ps, me) => {
|
super(meta, paramDef, async (ps, me, accessToken) => {
|
||||||
const query = this.registryItemsRepository.createQueryBuilder('item')
|
await this.registryApiService.remove(me.id, accessToken != null ? accessToken.id : (ps.domain ?? null), ps.scope, ps.key);
|
||||||
.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);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
/*
|
||||||
|
* 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<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
|
||||||
|
constructor(
|
||||||
|
private registryApiService: RegistryApiService,
|
||||||
|
) {
|
||||||
|
super(meta, paramDef, async (ps, me) => {
|
||||||
|
return await this.registryApiService.getAllScopeAndDomains(me.id);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,47 +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 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<typeof meta, typeof paramDef> { // 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;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -5,15 +5,10 @@
|
||||||
|
|
||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
import { Endpoint } from '@/server/api/endpoint-base.js';
|
import { Endpoint } from '@/server/api/endpoint-base.js';
|
||||||
import type { RegistryItemsRepository } from '@/models/_.js';
|
import { RegistryApiService } from '@/core/RegistryApiService.js';
|
||||||
import { IdService } from '@/core/IdService.js';
|
|
||||||
import { GlobalEventService } from '@/core/GlobalEventService.js';
|
|
||||||
import { DI } from '@/di-symbols.js';
|
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
requireCredential: true,
|
requireCredential: true,
|
||||||
|
|
||||||
secure: true,
|
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const paramDef = {
|
export const paramDef = {
|
||||||
|
@ -24,51 +19,18 @@ export const paramDef = {
|
||||||
scope: { type: 'array', default: [], items: {
|
scope: { type: 'array', default: [], items: {
|
||||||
type: 'string', pattern: /^[a-zA-Z0-9_]+$/.toString().slice(1, -1),
|
type: 'string', pattern: /^[a-zA-Z0-9_]+$/.toString().slice(1, -1),
|
||||||
} },
|
} },
|
||||||
|
domain: { type: 'string', nullable: true },
|
||||||
},
|
},
|
||||||
required: ['key', 'value'],
|
required: ['key', 'value', 'scope'],
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
|
export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(DI.registryItemsRepository)
|
private registryApiService: RegistryApiService,
|
||||||
private registryItemsRepository: RegistryItemsRepository,
|
|
||||||
|
|
||||||
private idService: IdService,
|
|
||||||
private globalEventService: GlobalEventService,
|
|
||||||
) {
|
) {
|
||||||
super(meta, paramDef, async (ps, me) => {
|
super(meta, paramDef, async (ps, me, accessToken) => {
|
||||||
const query = this.registryItemsRepository.createQueryBuilder('item')
|
await this.registryApiService.set(me.id, accessToken ? accessToken.id : (ps.domain ?? null), ps.scope, ps.key, ps.value);
|
||||||
.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,
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,7 +28,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<MkButton primary rounded inline @click="iLoveMisskey">I <Mfm text="$[jelly ❤]"/> #Misskey</MkButton>
|
<MkButton primary rounded inline @click="iLoveMisskey">I <Mfm text="$[jelly ❤]"/> #Misskey</MkButton>
|
||||||
</div>
|
</div>
|
||||||
<FormSection>
|
<FormSection>
|
||||||
<div class="_formLinks">
|
<div class="_gaps_s">
|
||||||
<FormLink to="https://github.com/misskey-dev/misskey" external>
|
<FormLink to="https://github.com/misskey-dev/misskey" external>
|
||||||
<template #icon><i class="ti ti-code"></i></template>
|
<template #icon><i class="ti ti-code"></i></template>
|
||||||
{{ i18n.ts._aboutMisskey.source }}
|
{{ i18n.ts._aboutMisskey.source }}
|
||||||
|
|
|
@ -47,7 +47,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
</MkKeyValue>
|
</MkKeyValue>
|
||||||
</FormSplit>
|
</FormSplit>
|
||||||
<FormLink v-if="instance.impressumUrl" :to="instance.impressumUrl" external>{{ i18n.ts.impressum }}</FormLink>
|
<FormLink v-if="instance.impressumUrl" :to="instance.impressumUrl" external>{{ i18n.ts.impressum }}</FormLink>
|
||||||
<div class="_formLinks">
|
<div class="_gaps_s">
|
||||||
<MkFolder v-if="instance.serverRules.length > 0">
|
<MkFolder v-if="instance.serverRules.length > 0">
|
||||||
<template #label>{{ i18n.ts.serverRules }}</template>
|
<template #label>{{ i18n.ts.serverRules }}</template>
|
||||||
|
|
||||||
|
@ -79,7 +79,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
<FormSection>
|
<FormSection>
|
||||||
<template #label>Well-known resources</template>
|
<template #label>Well-known resources</template>
|
||||||
<div class="_formLinks">
|
<div class="_gaps_s">
|
||||||
<FormLink :to="`/.well-known/host-meta`" external>host-meta</FormLink>
|
<FormLink :to="`/.well-known/host-meta`" external>host-meta</FormLink>
|
||||||
<FormLink :to="`/.well-known/host-meta.json`" external>host-meta.json</FormLink>
|
<FormLink :to="`/.well-known/host-meta.json`" external>host-meta.json</FormLink>
|
||||||
<FormLink :to="`/.well-known/nodeinfo`" external>nodeinfo</FormLink>
|
<FormLink :to="`/.well-known/nodeinfo`" external>nodeinfo</FormLink>
|
||||||
|
|
|
@ -11,7 +11,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<FormSplit>
|
<FormSplit>
|
||||||
<MkKeyValue>
|
<MkKeyValue>
|
||||||
<template #key>{{ i18n.ts._registry.domain }}</template>
|
<template #key>{{ i18n.ts._registry.domain }}</template>
|
||||||
<template #value>{{ i18n.ts.system }}</template>
|
<template #value>{{ props.domain === '@' ? i18n.ts.system : props.domain.toUpperCase() }}</template>
|
||||||
</MkKeyValue>
|
</MkKeyValue>
|
||||||
<MkKeyValue>
|
<MkKeyValue>
|
||||||
<template #key>{{ i18n.ts._registry.scope }}</template>
|
<template #key>{{ i18n.ts._registry.scope }}</template>
|
||||||
|
@ -23,8 +23,8 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
<FormSection v-if="keys">
|
<FormSection v-if="keys">
|
||||||
<template #label>{{ i18n.ts.keys }}</template>
|
<template #label>{{ i18n.ts.keys }}</template>
|
||||||
<div class="_formLinks">
|
<div class="_gaps_s">
|
||||||
<FormLink v-for="key in keys" :to="`/registry/value/system/${scope.join('/')}/${key[0]}`" class="_monospace">{{ key[0] }}<template #suffix>{{ key[1].toUpperCase() }}</template></FormLink>
|
<FormLink v-for="key in keys" :to="`/registry/value/${props.domain}/${scope.join('/')}/${key[0]}`" class="_monospace">{{ key[0] }}<template #suffix>{{ key[1].toUpperCase() }}</template></FormLink>
|
||||||
</div>
|
</div>
|
||||||
</FormSection>
|
</FormSection>
|
||||||
</div>
|
</div>
|
||||||
|
@ -46,15 +46,17 @@ import FormSplit from '@/components/form/split.vue';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
path: string;
|
path: string;
|
||||||
|
domain: string;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const scope = $computed(() => props.path.split('/'));
|
const scope = $computed(() => props.path ? props.path.split('/') : []);
|
||||||
|
|
||||||
let keys = $ref(null);
|
let keys = $ref(null);
|
||||||
|
|
||||||
function fetchKeys() {
|
function fetchKeys() {
|
||||||
os.api('i/registry/keys-with-type', {
|
os.api('i/registry/keys-with-type', {
|
||||||
scope: scope,
|
scope: scope,
|
||||||
|
domain: props.domain === '@' ? null : props.domain,
|
||||||
}).then(res => {
|
}).then(res => {
|
||||||
keys = Object.entries(res).sort((a, b) => a[0].localeCompare(b[0]));
|
keys = Object.entries(res).sort((a, b) => a[0].localeCompare(b[0]));
|
||||||
});
|
});
|
||||||
|
|
|
@ -14,7 +14,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<FormSplit>
|
<FormSplit>
|
||||||
<MkKeyValue>
|
<MkKeyValue>
|
||||||
<template #key>{{ i18n.ts._registry.domain }}</template>
|
<template #key>{{ i18n.ts._registry.domain }}</template>
|
||||||
<template #value>{{ i18n.ts.system }}</template>
|
<template #value>{{ props.domain === '@' ? i18n.ts.system : props.domain.toUpperCase() }}</template>
|
||||||
</MkKeyValue>
|
</MkKeyValue>
|
||||||
<MkKeyValue>
|
<MkKeyValue>
|
||||||
<template #key>{{ i18n.ts._registry.scope }}</template>
|
<template #key>{{ i18n.ts._registry.scope }}</template>
|
||||||
|
@ -58,6 +58,7 @@ import FormInfo from '@/components/MkInfo.vue';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
path: string;
|
path: string;
|
||||||
|
domain: string;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const scope = $computed(() => props.path.split('/').slice(0, -1));
|
const scope = $computed(() => props.path.split('/').slice(0, -1));
|
||||||
|
@ -70,6 +71,7 @@ function fetchValue() {
|
||||||
os.api('i/registry/get-detail', {
|
os.api('i/registry/get-detail', {
|
||||||
scope,
|
scope,
|
||||||
key,
|
key,
|
||||||
|
domain: props.domain === '@' ? null : props.domain,
|
||||||
}).then(res => {
|
}).then(res => {
|
||||||
value = res;
|
value = res;
|
||||||
valueForEditor = JSON5.stringify(res.value, null, '\t');
|
valueForEditor = JSON5.stringify(res.value, null, '\t');
|
||||||
|
@ -95,6 +97,7 @@ async function save() {
|
||||||
scope,
|
scope,
|
||||||
key,
|
key,
|
||||||
value: JSON5.parse(valueForEditor),
|
value: JSON5.parse(valueForEditor),
|
||||||
|
domain: props.domain === '@' ? null : props.domain,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -108,6 +111,7 @@ function del() {
|
||||||
os.apiWithDialog('i/registry/remove', {
|
os.apiWithDialog('i/registry/remove', {
|
||||||
scope,
|
scope,
|
||||||
key,
|
key,
|
||||||
|
domain: props.domain === '@' ? null : props.domain,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,12 +9,14 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<MkSpacer :contentMax="600" :marginMin="16">
|
<MkSpacer :contentMax="600" :marginMin="16">
|
||||||
<MkButton primary @click="createKey">{{ i18n.ts._registry.createKey }}</MkButton>
|
<MkButton primary @click="createKey">{{ i18n.ts._registry.createKey }}</MkButton>
|
||||||
|
|
||||||
<FormSection v-if="scopes">
|
<div v-if="scopesWithDomain" class="_gaps_m">
|
||||||
<template #label>{{ i18n.ts.system }}</template>
|
<FormSection v-for="domain in scopesWithDomain" :key="domain.domain">
|
||||||
<div class="_formLinks">
|
<template #label>{{ domain.domain ? domain.domain.toUpperCase() : i18n.ts.system }}</template>
|
||||||
<FormLink v-for="scope in scopes" :to="`/registry/keys/system/${scope.join('/')}`" class="_monospace">{{ scope.join('/') }}</FormLink>
|
<div class="_gaps_s">
|
||||||
|
<FormLink v-for="scope in domain.scopes" :to="`/registry/keys/${domain.domain ?? '@'}/${scope.join('/')}`" class="_monospace">{{ scope.length === 0 ? '(root)' : scope.join('/') }}</FormLink>
|
||||||
</div>
|
</div>
|
||||||
</FormSection>
|
</FormSection>
|
||||||
|
</div>
|
||||||
</MkSpacer>
|
</MkSpacer>
|
||||||
</MkStickyContainer>
|
</MkStickyContainer>
|
||||||
</template>
|
</template>
|
||||||
|
@ -28,11 +30,11 @@ import FormLink from '@/components/form/link.vue';
|
||||||
import FormSection from '@/components/form/section.vue';
|
import FormSection from '@/components/form/section.vue';
|
||||||
import MkButton from '@/components/MkButton.vue';
|
import MkButton from '@/components/MkButton.vue';
|
||||||
|
|
||||||
let scopes = $ref(null);
|
let scopesWithDomain = $ref(null);
|
||||||
|
|
||||||
function fetchScopes() {
|
function fetchScopes() {
|
||||||
os.api('i/registry/scopes').then(res => {
|
os.api('i/registry/scopes-with-domain').then(res => {
|
||||||
scopes = res.slice().sort((a, b) => a.join('/').localeCompare(b.join('/')));
|
scopesWithDomain = res;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { AsyncComponentLoader, defineAsyncComponent, inject } from 'vue';
|
import { AsyncComponentLoader, defineAsyncComponent, inject } from 'vue';
|
||||||
import { Router } from '@/nirax';
|
import { Router } from '@/nirax.js';
|
||||||
import { $i, iAmModerator } from '@/account.js';
|
import { $i, iAmModerator } from '@/account.js';
|
||||||
import MkLoading from '@/pages/_loading_.vue';
|
import MkLoading from '@/pages/_loading_.vue';
|
||||||
import MkError from '@/pages/_error_.vue';
|
import MkError from '@/pages/_error_.vue';
|
||||||
|
@ -318,10 +318,10 @@ export const routes = [{
|
||||||
name: 'avatarDecorations',
|
name: 'avatarDecorations',
|
||||||
component: page(() => import('./pages/avatar-decorations.vue')),
|
component: page(() => import('./pages/avatar-decorations.vue')),
|
||||||
}, {
|
}, {
|
||||||
path: '/registry/keys/system/:path(*)?',
|
path: '/registry/keys/:domain/:path(*)?',
|
||||||
component: page(() => import('./pages/registry.keys.vue')),
|
component: page(() => import('./pages/registry.keys.vue')),
|
||||||
}, {
|
}, {
|
||||||
path: '/registry/value/system/:path(*)?',
|
path: '/registry/value/:domain/:path(*)?',
|
||||||
component: page(() => import('./pages/registry.value.vue')),
|
component: page(() => import('./pages/registry.value.vue')),
|
||||||
}, {
|
}, {
|
||||||
path: '/registry',
|
path: '/registry',
|
||||||
|
|
|
@ -344,12 +344,6 @@ hr {
|
||||||
grid-gap: 12px;
|
grid-gap: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
._formLinks {
|
|
||||||
> *:not(:last-child) {
|
|
||||||
margin-bottom: 8px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
._beta {
|
._beta {
|
||||||
margin-left: 0.7em;
|
margin-left: 0.7em;
|
||||||
font-size: 65%;
|
font-size: 65%;
|
||||||
|
|
|
@ -1482,10 +1482,6 @@ export type Endpoints = {
|
||||||
};
|
};
|
||||||
res: null;
|
res: null;
|
||||||
};
|
};
|
||||||
'i/registry/scopes': {
|
|
||||||
req: NoParams;
|
|
||||||
res: string[][];
|
|
||||||
};
|
|
||||||
'i/registry/set': {
|
'i/registry/set': {
|
||||||
req: {
|
req: {
|
||||||
key: string;
|
key: string;
|
||||||
|
@ -3023,7 +3019,7 @@ type UserSorting = '+follower' | '-follower' | '+createdAt' | '-createdAt' | '+u
|
||||||
//
|
//
|
||||||
// src/api.types.ts:16:32 - (ae-forgotten-export) The symbol "TODO" needs to be exported by the entry point index.d.ts
|
// src/api.types.ts:16:32 - (ae-forgotten-export) The symbol "TODO" needs to be exported by the entry point index.d.ts
|
||||||
// src/api.types.ts:18:25 - (ae-forgotten-export) The symbol "NoParams" needs to be exported by the entry point index.d.ts
|
// src/api.types.ts:18:25 - (ae-forgotten-export) The symbol "NoParams" needs to be exported by the entry point index.d.ts
|
||||||
// src/api.types.ts:633:18 - (ae-forgotten-export) The symbol "ShowUserReq" needs to be exported by the entry point index.d.ts
|
// src/api.types.ts:632:18 - (ae-forgotten-export) The symbol "ShowUserReq" needs to be exported by the entry point index.d.ts
|
||||||
// src/entities.ts:116:2 - (ae-forgotten-export) The symbol "notificationTypes_2" needs to be exported by the entry point index.d.ts
|
// src/entities.ts:116:2 - (ae-forgotten-export) The symbol "notificationTypes_2" needs to be exported by the entry point index.d.ts
|
||||||
// src/entities.ts:612:2 - (ae-forgotten-export) The symbol "ModerationLogPayloads" needs to be exported by the entry point index.d.ts
|
// src/entities.ts:612:2 - (ae-forgotten-export) The symbol "ModerationLogPayloads" needs to be exported by the entry point index.d.ts
|
||||||
// src/streaming.types.ts:33:4 - (ae-forgotten-export) The symbol "FIXME" needs to be exported by the entry point index.d.ts
|
// src/streaming.types.ts:33:4 - (ae-forgotten-export) The symbol "FIXME" needs to be exported by the entry point index.d.ts
|
||||||
|
|
|
@ -399,7 +399,6 @@ export type Endpoints = {
|
||||||
'i/registry/keys-with-type': { req: { scope?: string[]; }; res: Record<string, 'null' | 'array' | 'number' | 'string' | 'boolean' | 'object'>; };
|
'i/registry/keys-with-type': { req: { scope?: string[]; }; res: Record<string, 'null' | 'array' | 'number' | 'string' | 'boolean' | 'object'>; };
|
||||||
'i/registry/keys': { req: { scope?: string[]; }; res: string[]; };
|
'i/registry/keys': { req: { scope?: string[]; }; res: string[]; };
|
||||||
'i/registry/remove': { req: { key: string; scope?: string[]; }; res: null; };
|
'i/registry/remove': { req: { key: string; scope?: string[]; }; res: null; };
|
||||||
'i/registry/scopes': { req: NoParams; res: string[][]; };
|
|
||||||
'i/registry/set': { req: { key: string; value: any; scope?: string[]; }; res: null; };
|
'i/registry/set': { req: { key: string; value: any; scope?: string[]; }; res: null; };
|
||||||
'i/revoke-token': { req: TODO; res: TODO; };
|
'i/revoke-token': { req: TODO; res: TODO; };
|
||||||
'i/signin-history': { req: { limit?: number; sinceId?: Signin['id']; untilId?: Signin['id']; }; res: Signin[]; };
|
'i/signin-history': { req: { limit?: number; sinceId?: Signin['id']; untilId?: Signin['id']; }; res: Signin[]; };
|
||||||
|
|
Loading…
Reference in a new issue