Merge remote-tracking branch 'mi-dev/develop' into emoji-req
This commit is contained in:
commit
36292549c5
157 changed files with 4057 additions and 2161 deletions
|
|
@ -86,6 +86,7 @@ export const ACHIEVEMENT_TYPES = [
|
|||
'cookieClicked',
|
||||
'brainDiver',
|
||||
'smashTestNotificationButton',
|
||||
'tutorialCompleted',
|
||||
] as const;
|
||||
|
||||
@Injectable()
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@ export class AnnouncementService {
|
|||
|
||||
const q = this.announcementsRepository.createQueryBuilder('announcement')
|
||||
.where('announcement.isActive = true')
|
||||
.andWhere('announcement.silence = false')
|
||||
.andWhere(new Brackets(qb => {
|
||||
qb.orWhere('announcement.userId = :userId', { userId: user.id });
|
||||
qb.orWhere('announcement.userId IS NULL');
|
||||
|
|
@ -73,6 +74,7 @@ export class AnnouncementService {
|
|||
icon: values.icon,
|
||||
display: values.display,
|
||||
forExistingUsers: values.forExistingUsers,
|
||||
silence: values.silence,
|
||||
needConfirmationToRead: values.needConfirmationToRead,
|
||||
userId: values.userId,
|
||||
}).then(x => this.announcementsRepository.findOneByOrFail(x.identifiers[0]));
|
||||
|
|
@ -124,6 +126,7 @@ export class AnnouncementService {
|
|||
display: values.display,
|
||||
icon: values.icon,
|
||||
forExistingUsers: values.forExistingUsers,
|
||||
silence: values.silence,
|
||||
needConfirmationToRead: values.needConfirmationToRead,
|
||||
isActive: values.isActive,
|
||||
});
|
||||
|
|
@ -210,6 +213,7 @@ export class AnnouncementService {
|
|||
icon: announcement.icon,
|
||||
display: announcement.display,
|
||||
needConfirmationToRead: announcement.needConfirmationToRead,
|
||||
silence: announcement.silence,
|
||||
forYou: announcement.userId === me?.id,
|
||||
isRead: reads.some(read => read.announcementId === announcement.id),
|
||||
}));
|
||||
|
|
|
|||
|
|
@ -64,6 +64,7 @@ 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,6 +197,7 @@ 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,6 +334,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
|
|||
FeaturedService,
|
||||
FunoutTimelineService,
|
||||
ChannelFollowingService,
|
||||
RegistryApiService,
|
||||
ChartLoggerService,
|
||||
FederationChart,
|
||||
NotesChart,
|
||||
|
|
@ -461,6 +464,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
|
|||
$FeaturedService,
|
||||
$FunoutTimelineService,
|
||||
$ChannelFollowingService,
|
||||
$RegistryApiService,
|
||||
$ChartLoggerService,
|
||||
$FederationChart,
|
||||
$NotesChart,
|
||||
|
|
@ -591,6 +595,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
|
|||
FeaturedService,
|
||||
FunoutTimelineService,
|
||||
ChannelFollowingService,
|
||||
RegistryApiService,
|
||||
FederationChart,
|
||||
NotesChart,
|
||||
UsersChart,
|
||||
|
|
@ -719,6 +724,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
|
|||
$FeaturedService,
|
||||
$FunoutTimelineService,
|
||||
$ChannelFollowingService,
|
||||
$RegistryApiService,
|
||||
$FederationChart,
|
||||
$NotesChart,
|
||||
$UsersChart,
|
||||
|
|
|
|||
|
|
@ -521,11 +521,13 @@ export class NoteCreateService implements OnApplicationShutdown {
|
|||
followeeId: user.id,
|
||||
notify: 'normal',
|
||||
}).then(followings => {
|
||||
for (const following of followings) {
|
||||
// TODO: ワードミュート考慮
|
||||
this.notificationService.createNotification(following.followerId, 'note', {
|
||||
noteId: note.id,
|
||||
}, user.id);
|
||||
if (note.visibility !== 'specified') {
|
||||
for (const following of followings) {
|
||||
// TODO: ワードミュート考慮
|
||||
this.notificationService.createNotification(following.followerId, 'note', {
|
||||
noteId: note.id,
|
||||
}, user.id);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
|||
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();
|
||||
}
|
||||
}
|
||||
|
|
@ -269,7 +269,7 @@ export class ApPersonService implements OnModuleInit {
|
|||
|
||||
const tags = extractApHashtags(person.tag).map(normalizeForSearch).splice(0, 32);
|
||||
|
||||
const isBot = getApType(object) === 'Service';
|
||||
const isBot = getApType(object) === 'Service' || getApType(object) === 'Application';
|
||||
|
||||
const bday = person['vcard:bday']?.match(/^\d{4}-\d{2}-\d{2}/);
|
||||
|
||||
|
|
@ -456,7 +456,7 @@ export class ApPersonService implements OnModuleInit {
|
|||
emojis: emojiNames,
|
||||
name: truncate(person.name, nameLength),
|
||||
tags,
|
||||
isBot: getApType(object) === 'Service',
|
||||
isBot: getApType(object) === 'Service' || getApType(object) === 'Application',
|
||||
isCat: (person as any).isCat === true,
|
||||
isLocked: person.manuallyApprovesFollowers,
|
||||
movedToUri: person.movedTo ?? null,
|
||||
|
|
|
|||
|
|
@ -85,6 +85,7 @@ export class ChannelEntityService {
|
|||
usersCount: channel.usersCount,
|
||||
notesCount: channel.notesCount,
|
||||
isSensitive: channel.isSensitive,
|
||||
allowRenoteToExternal: channel.allowRenoteToExternal,
|
||||
|
||||
...(me ? {
|
||||
isFollowing,
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@ export class InstanceEntityService {
|
|||
faviconUrl: instance.faviconUrl,
|
||||
themeColor: instance.themeColor,
|
||||
infoUpdatedAt: instance.infoUpdatedAt ? instance.infoUpdatedAt.toISOString() : null,
|
||||
latestRequestReceivedAt: instance.latestRequestReceivedAt ? instance.latestRequestReceivedAt.toISOString() : null,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -350,6 +350,7 @@ export class NoteEntityService implements OnModuleInit {
|
|||
name: channel.name,
|
||||
color: channel.color,
|
||||
isSensitive: channel.isSensitive,
|
||||
allowRenoteToExternal: channel.allowRenoteToExternal,
|
||||
} : undefined,
|
||||
mentions: note.mentions.length > 0 ? note.mentions : undefined,
|
||||
uri: note.uri ?? undefined,
|
||||
|
|
|
|||
|
|
@ -66,6 +66,12 @@ export class MiAnnouncement {
|
|||
})
|
||||
public forExistingUsers: boolean;
|
||||
|
||||
@Index()
|
||||
@Column('boolean', {
|
||||
default: false,
|
||||
})
|
||||
public silence: boolean;
|
||||
|
||||
@Index()
|
||||
@Column({
|
||||
...id(),
|
||||
|
|
|
|||
|
|
@ -93,4 +93,9 @@ export class MiChannel {
|
|||
default: false,
|
||||
})
|
||||
public isSensitive: boolean;
|
||||
|
||||
@Column('boolean', {
|
||||
default: true,
|
||||
})
|
||||
public allowRenoteToExternal: boolean;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -76,5 +76,9 @@ export const packedChannelSchema = {
|
|||
type: 'boolean',
|
||||
optional: false, nullable: false,
|
||||
},
|
||||
allowRenoteToExternal: {
|
||||
type: 'boolean',
|
||||
optional: false, nullable: false,
|
||||
},
|
||||
},
|
||||
} as const;
|
||||
|
|
|
|||
|
|
@ -103,5 +103,10 @@ export const packedFederationInstanceSchema = {
|
|||
optional: false, nullable: true,
|
||||
format: 'date-time',
|
||||
},
|
||||
latestRequestReceivedAt: {
|
||||
type: 'string',
|
||||
optional: false, nullable: true,
|
||||
format: 'date-time',
|
||||
},
|
||||
},
|
||||
} as const;
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import * as ep___admin_meta from './endpoints/admin/meta.js';
|
|||
import * as ep___admin_abuseUserReports from './endpoints/admin/abuse-user-reports.js';
|
||||
import * as ep___admin_accounts_create from './endpoints/admin/accounts/create.js';
|
||||
import * as ep___admin_accounts_delete from './endpoints/admin/accounts/delete.js';
|
||||
import * as ep___admin_accounts_findByEmail from './endpoints/admin/accounts/find-by-email.js';
|
||||
import * as ep___admin_ad_create from './endpoints/admin/ad/create.js';
|
||||
import * as ep___admin_ad_delete from './endpoints/admin/ad/delete.js';
|
||||
import * as ep___admin_ad_list from './endpoints/admin/ad/list.js';
|
||||
|
|
@ -233,7 +234,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_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_revokeToken from './endpoints/i/revoke-token.js';
|
||||
import * as ep___i_signinHistory from './endpoints/i/signin-history.js';
|
||||
|
|
@ -372,6 +373,7 @@ const $admin_meta: Provider = { provide: 'ep:admin/meta', useClass: ep___admin_m
|
|||
const $admin_abuseUserReports: Provider = { provide: 'ep:admin/abuse-user-reports', useClass: ep___admin_abuseUserReports.default };
|
||||
const $admin_accounts_create: Provider = { provide: 'ep:admin/accounts/create', useClass: ep___admin_accounts_create.default };
|
||||
const $admin_accounts_delete: Provider = { provide: 'ep:admin/accounts/delete', useClass: ep___admin_accounts_delete.default };
|
||||
const $admin_accounts_findByEmail: Provider = { provide: 'ep:admin/accounts/find-by-email', useClass: ep___admin_accounts_findByEmail.default };
|
||||
const $admin_ad_create: Provider = { provide: 'ep:admin/ad/create', useClass: ep___admin_ad_create.default };
|
||||
const $admin_ad_delete: Provider = { provide: 'ep:admin/ad/delete', useClass: ep___admin_ad_delete.default };
|
||||
const $admin_ad_list: Provider = { provide: 'ep:admin/ad/list', useClass: ep___admin_ad_list.default };
|
||||
|
|
@ -595,7 +597,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_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_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 };
|
||||
|
|
@ -738,6 +740,7 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention
|
|||
$admin_abuseUserReports,
|
||||
$admin_accounts_create,
|
||||
$admin_accounts_delete,
|
||||
$admin_accounts_findByEmail,
|
||||
$admin_ad_create,
|
||||
$admin_ad_delete,
|
||||
$admin_ad_list,
|
||||
|
|
@ -961,7 +964,7 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention
|
|||
$i_registry_keysWithType,
|
||||
$i_registry_keys,
|
||||
$i_registry_remove,
|
||||
$i_registry_scopes,
|
||||
$i_registry_scopesWithDomain,
|
||||
$i_registry_set,
|
||||
$i_revokeToken,
|
||||
$i_signinHistory,
|
||||
|
|
@ -1098,6 +1101,7 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention
|
|||
$admin_abuseUserReports,
|
||||
$admin_accounts_create,
|
||||
$admin_accounts_delete,
|
||||
$admin_accounts_findByEmail,
|
||||
$admin_ad_create,
|
||||
$admin_ad_delete,
|
||||
$admin_ad_list,
|
||||
|
|
@ -1321,7 +1325,7 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention
|
|||
$i_registry_keysWithType,
|
||||
$i_registry_keys,
|
||||
$i_registry_remove,
|
||||
$i_registry_scopes,
|
||||
$i_registry_scopesWithDomain,
|
||||
$i_registry_set,
|
||||
$i_revokeToken,
|
||||
$i_signinHistory,
|
||||
|
|
|
|||
|
|
@ -136,7 +136,20 @@ export class SignupApiService {
|
|||
return;
|
||||
}
|
||||
|
||||
if (ticket.usedAt) {
|
||||
// メアド認証が有効の場合
|
||||
if (instance.emailRequiredForSignup) {
|
||||
// メアド認証済みならエラー
|
||||
if (ticket.usedBy) {
|
||||
reply.code(400);
|
||||
return;
|
||||
}
|
||||
|
||||
// 認証しておらず、メール送信から30分以内ならエラー
|
||||
if (ticket.usedAt && ticket.usedAt.getTime() + (1000 * 60 * 30) > Date.now()) {
|
||||
reply.code(400);
|
||||
return;
|
||||
}
|
||||
} else if (ticket.usedAt) {
|
||||
reply.code(400);
|
||||
return;
|
||||
}
|
||||
|
|
@ -224,6 +237,10 @@ export class SignupApiService {
|
|||
try {
|
||||
const pendingUser = await this.userPendingsRepository.findOneByOrFail({ code });
|
||||
|
||||
if (this.idService.parse(pendingUser.id).date.getTime() + (1000 * 60 * 30) < Date.now()) {
|
||||
throw new FastifyReplyError(400, 'EXPIRED');
|
||||
}
|
||||
|
||||
const { account, secret } = await this.signupService.signup({
|
||||
username: pendingUser.username,
|
||||
passwordHash: pendingUser.password,
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import * as ep___admin_meta from './endpoints/admin/meta.js';
|
|||
import * as ep___admin_abuseUserReports from './endpoints/admin/abuse-user-reports.js';
|
||||
import * as ep___admin_accounts_create from './endpoints/admin/accounts/create.js';
|
||||
import * as ep___admin_accounts_delete from './endpoints/admin/accounts/delete.js';
|
||||
import * as ep___admin_accounts_findByEmail from './endpoints/admin/accounts/find-by-email.js';
|
||||
import * as ep___admin_ad_create from './endpoints/admin/ad/create.js';
|
||||
import * as ep___admin_ad_delete from './endpoints/admin/ad/delete.js';
|
||||
import * as ep___admin_ad_list from './endpoints/admin/ad/list.js';
|
||||
|
|
@ -233,7 +234,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_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_revokeToken from './endpoints/i/revoke-token.js';
|
||||
import * as ep___i_signinHistory from './endpoints/i/signin-history.js';
|
||||
|
|
@ -370,6 +371,7 @@ const eps = [
|
|||
['admin/abuse-user-reports', ep___admin_abuseUserReports],
|
||||
['admin/accounts/create', ep___admin_accounts_create],
|
||||
['admin/accounts/delete', ep___admin_accounts_delete],
|
||||
['admin/accounts/find-by-email', ep___admin_accounts_findByEmail],
|
||||
['admin/ad/create', ep___admin_ad_create],
|
||||
['admin/ad/delete', ep___admin_ad_delete],
|
||||
['admin/ad/list', ep___admin_ad_list],
|
||||
|
|
@ -593,7 +595,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', ep___i_registry_scopes],
|
||||
['i/registry/scopes-with-domain', ep___i_registry_scopesWithDomain],
|
||||
['i/registry/set', ep___i_registry_set],
|
||||
['i/revoke-token', ep___i_revokeToken],
|
||||
['i/signin-history', ep___i_signinHistory],
|
||||
|
|
|
|||
|
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* 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 { UserProfilesRepository } from '@/models/_.js';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import { UserEntityService } from '@/core/entities/UserEntityService.js';
|
||||
import { ApiError } from '@/server/api/error.js';
|
||||
|
||||
export const meta = {
|
||||
tags: ['admin'],
|
||||
|
||||
requireCredential: true,
|
||||
requireAdmin: true,
|
||||
|
||||
errors: {
|
||||
userNotFound: {
|
||||
message: 'No such user who has the email address.',
|
||||
code: 'USER_NOT_FOUND',
|
||||
id: 'cb865949-8af5-4062-a88c-ef55e8786d1d',
|
||||
},
|
||||
},
|
||||
} as const;
|
||||
|
||||
export const paramDef = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
email: { type: 'string' },
|
||||
},
|
||||
required: ['email'],
|
||||
} as const;
|
||||
|
||||
@Injectable()
|
||||
export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
|
||||
constructor(
|
||||
@Inject(DI.userProfilesRepository)
|
||||
private userProfilesRepository: UserProfilesRepository,
|
||||
|
||||
private userEntityService: UserEntityService,
|
||||
) {
|
||||
super(meta, paramDef, async (ps, me) => {
|
||||
const profile = await this.userProfilesRepository.findOne({
|
||||
where: { email: ps.email },
|
||||
relations: ['user'],
|
||||
});
|
||||
|
||||
if (profile == null) {
|
||||
throw new ApiError(meta.errors.userNotFound);
|
||||
}
|
||||
|
||||
const res = await this.userEntityService.pack(profile.user!, null, {
|
||||
detail: true,
|
||||
});
|
||||
|
||||
return res;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -58,6 +58,7 @@ export const paramDef = {
|
|||
icon: { type: 'string', enum: ['info', 'warning', 'error', 'success'], default: 'info' },
|
||||
display: { type: 'string', enum: ['normal', 'banner', 'dialog'], default: 'normal' },
|
||||
forExistingUsers: { type: 'boolean', default: false },
|
||||
silence: { type: 'boolean', default: false },
|
||||
needConfirmationToRead: { type: 'boolean', default: false },
|
||||
userId: { type: 'string', format: 'misskey:id', nullable: true, default: null },
|
||||
},
|
||||
|
|
@ -78,6 +79,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
icon: ps.icon,
|
||||
display: ps.display,
|
||||
forExistingUsers: ps.forExistingUsers,
|
||||
silence: ps.silence,
|
||||
needConfirmationToRead: ps.needConfirmationToRead,
|
||||
userId: ps.userId,
|
||||
}, me);
|
||||
|
|
|
|||
|
|
@ -86,6 +86,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
) {
|
||||
super(meta, paramDef, async (ps, me) => {
|
||||
const query = this.queryService.makePaginationQuery(this.announcementsRepository.createQueryBuilder('announcement'), ps.sinceId, ps.untilId);
|
||||
query.andWhere('announcement.isActive = true');
|
||||
if (ps.userId) {
|
||||
query.andWhere('announcement.userId = :userId', { userId: ps.userId });
|
||||
} else {
|
||||
|
|
@ -113,6 +114,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
display: announcement.display,
|
||||
isActive: announcement.isActive,
|
||||
forExistingUsers: announcement.forExistingUsers,
|
||||
silence: announcement.silence,
|
||||
needConfirmationToRead: announcement.needConfirmationToRead,
|
||||
userId: announcement.userId,
|
||||
reads: reads.get(announcement)!,
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ export const paramDef = {
|
|||
icon: { type: 'string', enum: ['info', 'warning', 'error', 'success'] },
|
||||
display: { type: 'string', enum: ['normal', 'banner', 'dialog'] },
|
||||
forExistingUsers: { type: 'boolean' },
|
||||
silence: { type: 'boolean' },
|
||||
needConfirmationToRead: { type: 'boolean' },
|
||||
isActive: { type: 'boolean' },
|
||||
},
|
||||
|
|
@ -63,6 +64,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
display: ps.display,
|
||||
icon: ps.icon,
|
||||
forExistingUsers: ps.forExistingUsers,
|
||||
silence: ps.silence,
|
||||
needConfirmationToRead: ps.needConfirmationToRead,
|
||||
isActive: ps.isActive,
|
||||
}, me);
|
||||
|
|
|
|||
|
|
@ -50,6 +50,7 @@ export const paramDef = {
|
|||
bannerId: { type: 'string', format: 'misskey:id', nullable: true },
|
||||
color: { type: 'string', minLength: 1, maxLength: 16 },
|
||||
isSensitive: { type: 'boolean', nullable: true },
|
||||
allowRenoteToExternal: { type: 'boolean', nullable: true },
|
||||
},
|
||||
required: ['name'],
|
||||
} as const;
|
||||
|
|
@ -87,6 +88,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
bannerId: banner ? banner.id : null,
|
||||
isSensitive: ps.isSensitive ?? false,
|
||||
...(ps.color !== undefined ? { color: ps.color } : {}),
|
||||
allowRenoteToExternal: ps.allowRenoteToExternal ?? true,
|
||||
} as MiChannel).then(x => this.channelsRepository.findOneByOrFail(x.identifiers[0]));
|
||||
|
||||
return await this.channelEntityService.pack(channel, me);
|
||||
|
|
|
|||
|
|
@ -61,6 +61,7 @@ export const paramDef = {
|
|||
},
|
||||
color: { type: 'string', minLength: 1, maxLength: 16 },
|
||||
isSensitive: { type: 'boolean', nullable: true },
|
||||
allowRenoteToExternal: { type: 'boolean', nullable: true },
|
||||
},
|
||||
required: ['channelId'],
|
||||
} as const;
|
||||
|
|
@ -115,6 +116,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
...(typeof ps.isArchived === 'boolean' ? { isArchived: ps.isArchived } : {}),
|
||||
...(banner ? { bannerId: banner.id } : {}),
|
||||
...(typeof ps.isSensitive === 'boolean' ? { isSensitive: ps.isSensitive } : {}),
|
||||
...(typeof ps.allowRenoteToExternal === 'boolean' ? { allowRenoteToExternal: ps.allowRenoteToExternal } : {}),
|
||||
});
|
||||
|
||||
return await this.channelEntityService.pack(channel.id, me);
|
||||
|
|
|
|||
|
|
@ -5,13 +5,10 @@
|
|||
|
||||
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';
|
||||
|
||||
export const meta = {
|
||||
requireCredential: true,
|
||||
|
||||
secure: true,
|
||||
} as const;
|
||||
|
||||
export const paramDef = {
|
||||
|
|
@ -20,23 +17,18 @@ 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: [],
|
||||
required: ['scope'],
|
||||
} 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,
|
||||
private registryApiService: RegistryApiService,
|
||||
) {
|
||||
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();
|
||||
super(meta, paramDef, async (ps, me, accessToken) => {
|
||||
const items = await this.registryApiService.getAllItemsOfScope(me.id, accessToken != null ? accessToken.id : (ps.domain ?? null), ps.scope);
|
||||
|
||||
const res = {} as Record<string, any>;
|
||||
|
||||
|
|
|
|||
|
|
@ -5,15 +5,12 @@
|
|||
|
||||
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.',
|
||||
|
|
@ -30,24 +27,18 @@ 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'],
|
||||
required: ['key', 'scope'],
|
||||
} 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,
|
||||
private registryApiService: RegistryApiService,
|
||||
) {
|
||||
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();
|
||||
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);
|
||||
|
||||
if (item == null) {
|
||||
throw new ApiError(meta.errors.noSuchKey);
|
||||
|
|
|
|||
|
|
@ -5,15 +5,12 @@
|
|||
|
||||
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.',
|
||||
|
|
@ -30,24 +27,18 @@ 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'],
|
||||
required: ['key', 'scope'],
|
||||
} 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,
|
||||
private registryApiService: RegistryApiService,
|
||||
) {
|
||||
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();
|
||||
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);
|
||||
|
||||
if (item == null) {
|
||||
throw new ApiError(meta.errors.noSuchKey);
|
||||
|
|
|
|||
|
|
@ -5,13 +5,10 @@
|
|||
|
||||
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';
|
||||
|
||||
export const meta = {
|
||||
requireCredential: true,
|
||||
|
||||
secure: true,
|
||||
} as const;
|
||||
|
||||
export const paramDef = {
|
||||
|
|
@ -20,36 +17,31 @@ 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: [],
|
||||
required: ['scope'],
|
||||
} 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,
|
||||
private registryApiService: RegistryApiService,
|
||||
) {
|
||||
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();
|
||||
super(meta, paramDef, async (ps, me, accessToken) => {
|
||||
const items = await this.registryApiService.getAllItemsOfScope(me.id, accessToken != null ? accessToken.id : (ps.domain ?? null), ps.scope);
|
||||
|
||||
const res = {} as Record<string, string>;
|
||||
|
||||
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;
|
||||
|
|
|
|||
|
|
@ -5,13 +5,10 @@
|
|||
|
||||
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';
|
||||
|
||||
export const meta = {
|
||||
requireCredential: true,
|
||||
|
||||
secure: true,
|
||||
} as const;
|
||||
|
||||
export const paramDef = {
|
||||
|
|
@ -20,26 +17,18 @@ 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: [],
|
||||
required: ['scope'],
|
||||
} 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,
|
||||
private registryApiService: RegistryApiService,
|
||||
) {
|
||||
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);
|
||||
super(meta, paramDef, async (ps, me, accessToken) => {
|
||||
return await this.registryApiService.getAllKeysOfScope(me.id, accessToken != null ? accessToken.id : (ps.domain ?? null), ps.scope);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,13 +7,12 @@ 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.',
|
||||
|
|
@ -30,30 +29,18 @@ 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'],
|
||||
required: ['key', 'scope'],
|
||||
} 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,
|
||||
private registryApiService: RegistryApiService,
|
||||
) {
|
||||
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);
|
||||
super(meta, paramDef, async (ps, me, accessToken) => {
|
||||
await this.registryApiService.remove(me.id, accessToken != null ? accessToken.id : (ps.domain ?? null), ps.scope, ps.key);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 { Endpoint } from '@/server/api/endpoint-base.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';
|
||||
import { RegistryApiService } from '@/core/RegistryApiService.js';
|
||||
|
||||
export const meta = {
|
||||
requireCredential: true,
|
||||
|
||||
secure: true,
|
||||
} as const;
|
||||
|
||||
export const paramDef = {
|
||||
|
|
@ -24,51 +19,18 @@ 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'],
|
||||
required: ['key', 'value', 'scope'],
|
||||
} 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,
|
||||
|
||||
private idService: IdService,
|
||||
private globalEventService: GlobalEventService,
|
||||
private registryApiService: RegistryApiService,
|
||||
) {
|
||||
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,
|
||||
});
|
||||
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);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ export const paramDef = {
|
|||
type: 'object',
|
||||
properties: {
|
||||
tokenId: { type: 'string', format: 'misskey:id' },
|
||||
token: { type: 'string' },
|
||||
token: { type: 'string', nullable: true },
|
||||
},
|
||||
anyOf: [
|
||||
{ required: ['tokenId'] },
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ describe('api:notes/create', () => {
|
|||
|
||||
test('0 characters cw', () => {
|
||||
expect(v({ text: 'Body', cw: '' }))
|
||||
.toBe(VALID);
|
||||
.toBe(INVALID);
|
||||
});
|
||||
|
||||
test('reject only cw', () => {
|
||||
|
|
|
|||
|
|
@ -99,6 +99,12 @@ export const meta = {
|
|||
code: 'NO_SUCH_FILE',
|
||||
id: 'b6992544-63e7-67f0-fa7f-32444b1b5306',
|
||||
},
|
||||
|
||||
cannotRenoteOutsideOfChannel: {
|
||||
message: 'Cannot renote outside of channel.',
|
||||
code: 'CANNOT_RENOTE_OUTSIDE_OF_CHANNEL',
|
||||
id: '33510210-8452-094c-6227-4a6c05d99f00',
|
||||
},
|
||||
},
|
||||
} as const;
|
||||
|
||||
|
|
@ -246,6 +252,19 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
// specified / direct noteはreject
|
||||
throw new ApiError(meta.errors.cannotRenoteDueToVisibility);
|
||||
}
|
||||
|
||||
if (renote.channelId && renote.channelId !== ps.channelId) {
|
||||
// チャンネルのノートに対しリノート要求がきたとき、チャンネル外へのリノート可否をチェック
|
||||
// リノートのユースケースのうち、チャンネル内→チャンネル外は少数だと考えられるため、JOINはせず必要な時に都度取得する
|
||||
const renoteChannel = await this.channelsRepository.findOneById(renote.channelId);
|
||||
if (renoteChannel == null) {
|
||||
// リノートしたいノートが書き込まれているチャンネルが無い
|
||||
throw new ApiError(meta.errors.noSuchChannel);
|
||||
} else if (!renoteChannel.allowRenoteToExternal) {
|
||||
// リノート作成のリクエストだが、対象チャンネルがリノート禁止だった場合
|
||||
throw new ApiError(meta.errors.cannotRenoteOutsideOfChannel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let reply: MiNote | null = null;
|
||||
|
|
|
|||
|
|
@ -87,6 +87,16 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
if (ps.withFiles) {
|
||||
query.andWhere('note.fileIds != \'{}\'');
|
||||
}
|
||||
|
||||
if (ps.withRenotes === false) {
|
||||
query.andWhere(new Brackets(qb => {
|
||||
qb.where('note.renoteId IS NULL');
|
||||
qb.orWhere(new Brackets(qb => {
|
||||
qb.where('note.text IS NOT NULL');
|
||||
qb.orWhere('note.fileIds != \'{}\'');
|
||||
}));
|
||||
}));
|
||||
}
|
||||
//#endregion
|
||||
|
||||
const timeline = await query.limit(ps.limit).getMany();
|
||||
|
|
|
|||
|
|
@ -51,7 +51,6 @@ export const paramDef = {
|
|||
untilId: { type: 'string', format: 'misskey:id' },
|
||||
sinceDate: { type: 'integer' },
|
||||
untilDate: { type: 'integer' },
|
||||
includeMyRenotes: { type: 'boolean', default: true },
|
||||
withFiles: { type: 'boolean', default: false },
|
||||
excludeNsfw: { type: 'boolean', default: false },
|
||||
},
|
||||
|
|
@ -169,7 +168,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
query.andWhere('note.fileIds != \'{}\'');
|
||||
}
|
||||
|
||||
if (ps.includeMyRenotes === false) {
|
||||
if (ps.withRenotes === false) {
|
||||
query.andWhere(new Brackets(qb => {
|
||||
qb.orWhere('note.userId != :userId', { userId: ps.userId });
|
||||
qb.orWhere('note.renoteId IS NULL');
|
||||
|
|
|
|||
|
|
@ -175,6 +175,7 @@ export class ClientServerService {
|
|||
serverErrorImageUrl: meta.serverErrorImageUrl ?? 'https://xn--931a.moe/assets/error.jpg',
|
||||
infoImageUrl: meta.infoImageUrl ?? 'https://xn--931a.moe/assets/info.jpg',
|
||||
notFoundImageUrl: meta.notFoundImageUrl ?? 'https://xn--931a.moe/assets/not-found.jpg',
|
||||
instanceUrl: this.config.url,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -253,8 +254,9 @@ export class ClientServerService {
|
|||
decorateReply: false,
|
||||
});
|
||||
} else {
|
||||
const port = (process.env.VITE_PORT ?? '5173');
|
||||
fastify.register(fastifyProxy, {
|
||||
upstream: 'http://localhost:5173', // TODO: port configuration
|
||||
upstream: 'http://localhost:' + port,
|
||||
prefix: '/vite',
|
||||
rewritePrefix: '/vite',
|
||||
});
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ html
|
|||
meta(name='theme-color' content= themeColor || '#86b300')
|
||||
meta(name='theme-color-orig' content= themeColor || '#86b300')
|
||||
meta(property='og:site_name' content= instanceName || 'Misskey')
|
||||
meta(property='instance_url' content= instanceUrl)
|
||||
meta(name='viewport' content='width=device-width, initial-scale=1')
|
||||
link(rel='icon' href= icon || '/favicon.ico')
|
||||
link(rel='apple-touch-icon' href= appleTouchIcon || '/apple-touch-icon.png')
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue