Merge branch 'develop' into mkusername-empty
This commit is contained in:
commit
cf2b1c1e00
|
@ -61,7 +61,7 @@ export class CustomEmojiService {
|
||||||
await this.db.queryResultCache!.remove(['meta_emojis']);
|
await this.db.queryResultCache!.remove(['meta_emojis']);
|
||||||
|
|
||||||
this.globalEventService.publishBroadcastStream('emojiAdded', {
|
this.globalEventService.publishBroadcastStream('emojiAdded', {
|
||||||
emoji: await this.emojiEntityService.pack(emoji.id),
|
emoji: await this.emojiEntityService.packDetailed(emoji.id),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,44 +5,59 @@ import type { Packed } from '@/misc/schema.js';
|
||||||
import type { } from '@/models/entities/Blocking.js';
|
import type { } from '@/models/entities/Blocking.js';
|
||||||
import type { Emoji } from '@/models/entities/Emoji.js';
|
import type { Emoji } from '@/models/entities/Emoji.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
import { UserEntityService } from './UserEntityService.js';
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class EmojiEntityService {
|
export class EmojiEntityService {
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(DI.emojisRepository)
|
@Inject(DI.emojisRepository)
|
||||||
private emojisRepository: EmojisRepository,
|
private emojisRepository: EmojisRepository,
|
||||||
|
|
||||||
private userEntityService: UserEntityService,
|
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async pack(
|
public async packSimple(
|
||||||
src: Emoji['id'] | Emoji,
|
src: Emoji['id'] | Emoji,
|
||||||
opts: { omitHost?: boolean; omitId?: boolean; withUrl?: boolean; } = { omitHost: true, omitId: true, withUrl: true },
|
): Promise<Packed<'EmojiSimple'>> {
|
||||||
): Promise<Packed<'Emoji'>> {
|
|
||||||
opts = { omitHost: true, omitId: true, withUrl: true, ...opts };
|
|
||||||
|
|
||||||
const emoji = typeof src === 'object' ? src : await this.emojisRepository.findOneByOrFail({ id: src });
|
const emoji = typeof src === 'object' ? src : await this.emojisRepository.findOneByOrFail({ id: src });
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: opts.omitId ? undefined : emoji.id,
|
|
||||||
aliases: emoji.aliases,
|
aliases: emoji.aliases,
|
||||||
name: emoji.name,
|
name: emoji.name,
|
||||||
category: emoji.category,
|
category: emoji.category,
|
||||||
host: opts.omitHost ? undefined : emoji.host,
|
|
||||||
// || emoji.originalUrl してるのは後方互換性のため(publicUrlはstringなので??はだめ)
|
// || emoji.originalUrl してるのは後方互換性のため(publicUrlはstringなので??はだめ)
|
||||||
url: opts.withUrl ? (emoji.publicUrl || emoji.originalUrl) : undefined,
|
url: emoji.publicUrl || emoji.originalUrl,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public packMany(
|
public packSimpleMany(
|
||||||
emojis: any[],
|
emojis: any[],
|
||||||
opts: { omitHost?: boolean; omitId?: boolean; withUrl?: boolean; } = {},
|
|
||||||
) {
|
) {
|
||||||
return Promise.all(emojis.map(x => this.pack(x, opts)));
|
return Promise.all(emojis.map(x => this.packSimple(x)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@bindThis
|
||||||
|
public async packDetailed(
|
||||||
|
src: Emoji['id'] | Emoji,
|
||||||
|
): Promise<Packed<'EmojiDetailed'>> {
|
||||||
|
const emoji = typeof src === 'object' ? src : await this.emojisRepository.findOneByOrFail({ id: src });
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: emoji.id,
|
||||||
|
aliases: emoji.aliases,
|
||||||
|
name: emoji.name,
|
||||||
|
category: emoji.category,
|
||||||
|
host: emoji.host,
|
||||||
|
// || emoji.originalUrl してるのは後方互換性のため(publicUrlはstringなので??はだめ)
|
||||||
|
url: emoji.publicUrl || emoji.originalUrl,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@bindThis
|
||||||
|
public packDetailedMany(
|
||||||
|
emojis: any[],
|
||||||
|
) {
|
||||||
|
return Promise.all(emojis.map(x => this.packDetailed(x)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,7 @@ import { packedClipSchema } from '@/models/schema/clip.js';
|
||||||
import { packedFederationInstanceSchema } from '@/models/schema/federation-instance.js';
|
import { packedFederationInstanceSchema } from '@/models/schema/federation-instance.js';
|
||||||
import { packedQueueCountSchema } from '@/models/schema/queue.js';
|
import { packedQueueCountSchema } from '@/models/schema/queue.js';
|
||||||
import { packedGalleryPostSchema } from '@/models/schema/gallery-post.js';
|
import { packedGalleryPostSchema } from '@/models/schema/gallery-post.js';
|
||||||
import { packedEmojiSchema } from '@/models/schema/emoji.js';
|
import { packedEmojiDetailedSchema, packedEmojiSimpleSchema } from '@/models/schema/emoji.js';
|
||||||
import { packedFlashSchema } from '@/models/schema/flash.js';
|
import { packedFlashSchema } from '@/models/schema/flash.js';
|
||||||
|
|
||||||
export const refs = {
|
export const refs = {
|
||||||
|
@ -57,7 +57,8 @@ export const refs = {
|
||||||
Clip: packedClipSchema,
|
Clip: packedClipSchema,
|
||||||
FederationInstance: packedFederationInstanceSchema,
|
FederationInstance: packedFederationInstanceSchema,
|
||||||
GalleryPost: packedGalleryPostSchema,
|
GalleryPost: packedGalleryPostSchema,
|
||||||
Emoji: packedEmojiSchema,
|
EmojiSimple: packedEmojiSimpleSchema,
|
||||||
|
EmojiDetailed: packedEmojiDetailedSchema,
|
||||||
Flash: packedFlashSchema,
|
Flash: packedFlashSchema,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,37 @@
|
||||||
export const packedEmojiSchema = {
|
export const packedEmojiSimpleSchema = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
aliases: {
|
||||||
|
type: 'array',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
items: {
|
||||||
|
type: 'string',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
format: 'id',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
name: {
|
||||||
|
type: 'string',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
},
|
||||||
|
category: {
|
||||||
|
type: 'string',
|
||||||
|
optional: false, nullable: true,
|
||||||
|
},
|
||||||
|
url: {
|
||||||
|
type: 'string',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
export const packedEmojiDetailedSchema = {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
properties: {
|
properties: {
|
||||||
id: {
|
id: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
optional: true, nullable: false,
|
optional: false, nullable: false,
|
||||||
format: 'id',
|
format: 'id',
|
||||||
example: 'xxxxxxxxxx',
|
|
||||||
},
|
},
|
||||||
aliases: {
|
aliases: {
|
||||||
type: 'array',
|
type: 'array',
|
||||||
|
@ -26,12 +52,12 @@ export const packedEmojiSchema = {
|
||||||
},
|
},
|
||||||
host: {
|
host: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
optional: true, nullable: true,
|
optional: false, nullable: true,
|
||||||
description: 'The local host is represented with `null`.',
|
description: 'The local host is represented with `null`.',
|
||||||
},
|
},
|
||||||
url: {
|
url: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
optional: true, nullable: false,
|
optional: false, nullable: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
} as const;
|
} as const;
|
||||||
|
|
|
@ -219,8 +219,8 @@ export class ApiCallService implements OnApplicationShutdown {
|
||||||
|
|
||||||
const limit = Object.assign({}, ep.meta.limit);
|
const limit = Object.assign({}, ep.meta.limit);
|
||||||
|
|
||||||
if (!limit.key) {
|
if (limit.key == null) {
|
||||||
limit.key = ep.name;
|
(limit as any).key = ep.name;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: 毎リクエスト計算するのもあれだしキャッシュしたい
|
// TODO: 毎リクエスト計算するのもあれだしキャッシュしたい
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import type { Schema } from '@/misc/schema.js';
|
import type { Schema } from '@/misc/schema.js';
|
||||||
|
import { RolePolicies } from '@/core/RoleService.js';
|
||||||
|
|
||||||
import * as ep___admin_meta from './endpoints/admin/meta.js';
|
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_abuseUserReports from './endpoints/admin/abuse-user-reports.js';
|
||||||
|
@ -659,7 +660,7 @@ export interface IEndpointMeta {
|
||||||
*/
|
*/
|
||||||
readonly requireAdmin?: boolean;
|
readonly requireAdmin?: boolean;
|
||||||
|
|
||||||
readonly requireRolePolicy?: string;
|
readonly requireRolePolicy?: keyof RolePolicies;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* エンドポイントのリミテーションに関するやつ
|
* エンドポイントのリミテーションに関するやつ
|
||||||
|
|
|
@ -56,7 +56,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
|
||||||
await this.db.queryResultCache!.remove(['meta_emojis']);
|
await this.db.queryResultCache!.remove(['meta_emojis']);
|
||||||
|
|
||||||
this.globalEventService.publishBroadcastStream('emojiUpdated', {
|
this.globalEventService.publishBroadcastStream('emojiUpdated', {
|
||||||
emojis: await this.emojiEntityService.packMany(ps.ids),
|
emojis: await this.emojiEntityService.packDetailedMany(ps.ids),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -92,7 +92,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
|
||||||
await this.db.queryResultCache!.remove(['meta_emojis']);
|
await this.db.queryResultCache!.remove(['meta_emojis']);
|
||||||
|
|
||||||
this.globalEventService.publishBroadcastStream('emojiAdded', {
|
this.globalEventService.publishBroadcastStream('emojiAdded', {
|
||||||
emoji: await this.emojiEntityService.pack(copied.id),
|
emoji: await this.emojiEntityService.packDetailed(copied.id),
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -54,7 +54,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
|
||||||
}
|
}
|
||||||
|
|
||||||
this.globalEventService.publishBroadcastStream('emojiDeleted', {
|
this.globalEventService.publishBroadcastStream('emojiDeleted', {
|
||||||
emojis: await this.emojiEntityService.packMany(emojis),
|
emojis: await this.emojiEntityService.packDetailedMany(emojis),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,9 +4,9 @@ import { Endpoint } from '@/server/api/endpoint-base.js';
|
||||||
import type { EmojisRepository } from '@/models/index.js';
|
import type { EmojisRepository } from '@/models/index.js';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import { ModerationLogService } from '@/core/ModerationLogService.js';
|
import { ModerationLogService } from '@/core/ModerationLogService.js';
|
||||||
import { ApiError } from '../../../error.js';
|
|
||||||
import { EmojiEntityService } from '@/core/entities/EmojiEntityService.js';
|
import { EmojiEntityService } from '@/core/entities/EmojiEntityService.js';
|
||||||
import { GlobalEventService } from '@/core/GlobalEventService.js';
|
import { GlobalEventService } from '@/core/GlobalEventService.js';
|
||||||
|
import { ApiError } from '../../../error.js';
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
tags: ['admin'],
|
tags: ['admin'],
|
||||||
|
@ -57,7 +57,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
|
||||||
await this.db.queryResultCache!.remove(['meta_emojis']);
|
await this.db.queryResultCache!.remove(['meta_emojis']);
|
||||||
|
|
||||||
this.globalEventService.publishBroadcastStream('emojiDeleted', {
|
this.globalEventService.publishBroadcastStream('emojiDeleted', {
|
||||||
emojis: [await this.emojiEntityService.pack(emoji)],
|
emojis: [await this.emojiEntityService.packDetailed(emoji)],
|
||||||
});
|
});
|
||||||
|
|
||||||
this.moderationLogService.insertModerationLog(me, 'deleteEmoji', {
|
this.moderationLogService.insertModerationLog(me, 'deleteEmoji', {
|
||||||
|
|
|
@ -101,7 +101,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
|
||||||
.take(ps.limit)
|
.take(ps.limit)
|
||||||
.getMany();
|
.getMany();
|
||||||
|
|
||||||
return this.emojiEntityService.packMany(emojis, { omitHost: false, omitId: false, withUrl: false });
|
return this.emojiEntityService.packDetailedMany(emojis);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -98,7 +98,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
|
||||||
emojis = await q.take(ps.limit).getMany();
|
emojis = await q.take(ps.limit).getMany();
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.emojiEntityService.packMany(emojis, { omitHost: false, omitId: false, withUrl: false });
|
return this.emojiEntityService.packDetailedMany(emojis);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,7 +56,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
|
||||||
await this.db.queryResultCache!.remove(['meta_emojis']);
|
await this.db.queryResultCache!.remove(['meta_emojis']);
|
||||||
|
|
||||||
this.globalEventService.publishBroadcastStream('emojiUpdated', {
|
this.globalEventService.publishBroadcastStream('emojiUpdated', {
|
||||||
emojis: await this.emojiEntityService.packMany(ps.ids),
|
emojis: await this.emojiEntityService.packDetailedMany(ps.ids),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,7 +52,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
|
||||||
await this.db.queryResultCache!.remove(['meta_emojis']);
|
await this.db.queryResultCache!.remove(['meta_emojis']);
|
||||||
|
|
||||||
this.globalEventService.publishBroadcastStream('emojiUpdated', {
|
this.globalEventService.publishBroadcastStream('emojiUpdated', {
|
||||||
emojis: await this.emojiEntityService.packMany(ps.ids),
|
emojis: await this.emojiEntityService.packDetailedMany(ps.ids),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,7 +54,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
|
||||||
await this.db.queryResultCache!.remove(['meta_emojis']);
|
await this.db.queryResultCache!.remove(['meta_emojis']);
|
||||||
|
|
||||||
this.globalEventService.publishBroadcastStream('emojiUpdated', {
|
this.globalEventService.publishBroadcastStream('emojiUpdated', {
|
||||||
emojis: await this.emojiEntityService.packMany(ps.ids),
|
emojis: await this.emojiEntityService.packDetailedMany(ps.ids),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,9 +3,9 @@ import { DataSource } from 'typeorm';
|
||||||
import { Endpoint } from '@/server/api/endpoint-base.js';
|
import { Endpoint } from '@/server/api/endpoint-base.js';
|
||||||
import type { EmojisRepository } from '@/models/index.js';
|
import type { EmojisRepository } from '@/models/index.js';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import { ApiError } from '../../../error.js';
|
|
||||||
import { EmojiEntityService } from '@/core/entities/EmojiEntityService.js';
|
import { EmojiEntityService } from '@/core/entities/EmojiEntityService.js';
|
||||||
import { GlobalEventService } from '@/core/GlobalEventService.js';
|
import { GlobalEventService } from '@/core/GlobalEventService.js';
|
||||||
|
import { ApiError } from '../../../error.js';
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
tags: ['admin'],
|
tags: ['admin'],
|
||||||
|
@ -68,7 +68,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
|
||||||
|
|
||||||
await this.db.queryResultCache!.remove(['meta_emojis']);
|
await this.db.queryResultCache!.remove(['meta_emojis']);
|
||||||
|
|
||||||
const updated = await this.emojiEntityService.pack(emoji.id);
|
const updated = await this.emojiEntityService.packDetailed(emoji.id);
|
||||||
|
|
||||||
if (emoji.name === ps.name) {
|
if (emoji.name === ps.name) {
|
||||||
this.globalEventService.publishBroadcastStream('emojiUpdated', {
|
this.globalEventService.publishBroadcastStream('emojiUpdated', {
|
||||||
|
@ -76,7 +76,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
this.globalEventService.publishBroadcastStream('emojiDeleted', {
|
this.globalEventService.publishBroadcastStream('emojiDeleted', {
|
||||||
emojis: [await this.emojiEntityService.pack(emoji)],
|
emojis: [await this.emojiEntityService.packDetailed(emoji)],
|
||||||
});
|
});
|
||||||
|
|
||||||
this.globalEventService.publishBroadcastStream('emojiAdded', {
|
this.globalEventService.publishBroadcastStream('emojiAdded', {
|
||||||
|
|
|
@ -54,86 +54,22 @@ export const meta = {
|
||||||
},
|
},
|
||||||
mascotImageUrl: {
|
mascotImageUrl: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
optional: false, nullable: false,
|
optional: false, nullable: true,
|
||||||
default: '/assets/ai.png',
|
default: '/assets/ai.png',
|
||||||
},
|
},
|
||||||
bannerUrl: {
|
bannerUrl: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
optional: false, nullable: false,
|
optional: false, nullable: true,
|
||||||
},
|
},
|
||||||
errorImageUrl: {
|
errorImageUrl: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
optional: false, nullable: false,
|
optional: false, nullable: true,
|
||||||
default: 'https://xn--931a.moe/aiart/yubitun.png',
|
default: 'https://xn--931a.moe/aiart/yubitun.png',
|
||||||
},
|
},
|
||||||
iconUrl: {
|
iconUrl: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
optional: false, nullable: true,
|
optional: false, nullable: true,
|
||||||
},
|
},
|
||||||
maxNoteTextLength: {
|
|
||||||
type: 'number',
|
|
||||||
optional: false, nullable: false,
|
|
||||||
},
|
|
||||||
emojis: {
|
|
||||||
type: 'array',
|
|
||||||
optional: false, nullable: false,
|
|
||||||
items: {
|
|
||||||
type: 'object',
|
|
||||||
optional: false, nullable: false,
|
|
||||||
properties: {
|
|
||||||
id: {
|
|
||||||
type: 'string',
|
|
||||||
optional: false, nullable: false,
|
|
||||||
format: 'id',
|
|
||||||
},
|
|
||||||
aliases: {
|
|
||||||
type: 'array',
|
|
||||||
optional: false, nullable: false,
|
|
||||||
items: {
|
|
||||||
type: 'string',
|
|
||||||
optional: false, nullable: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
category: {
|
|
||||||
type: 'string',
|
|
||||||
optional: false, nullable: true,
|
|
||||||
},
|
|
||||||
host: {
|
|
||||||
type: 'string',
|
|
||||||
optional: false, nullable: true,
|
|
||||||
},
|
|
||||||
url: {
|
|
||||||
type: 'string',
|
|
||||||
optional: false, nullable: false,
|
|
||||||
format: 'url',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
ads: {
|
|
||||||
type: 'array',
|
|
||||||
optional: false, nullable: false,
|
|
||||||
items: {
|
|
||||||
type: 'object',
|
|
||||||
optional: false, nullable: false,
|
|
||||||
properties: {
|
|
||||||
place: {
|
|
||||||
type: 'string',
|
|
||||||
optional: false, nullable: false,
|
|
||||||
},
|
|
||||||
url: {
|
|
||||||
type: 'string',
|
|
||||||
optional: false, nullable: false,
|
|
||||||
format: 'url',
|
|
||||||
},
|
|
||||||
imageUrl: {
|
|
||||||
type: 'string',
|
|
||||||
optional: false, nullable: false,
|
|
||||||
format: 'url',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
enableEmail: {
|
enableEmail: {
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
optional: false, nullable: false,
|
optional: false, nullable: false,
|
||||||
|
@ -146,10 +82,6 @@ export const meta = {
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
optional: false, nullable: false,
|
optional: false, nullable: false,
|
||||||
},
|
},
|
||||||
proxyAccountName: {
|
|
||||||
type: 'string',
|
|
||||||
optional: false, nullable: true,
|
|
||||||
},
|
|
||||||
userStarForReactionFallback: {
|
userStarForReactionFallback: {
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
optional: true, nullable: false,
|
optional: true, nullable: false,
|
||||||
|
@ -228,7 +160,7 @@ export const meta = {
|
||||||
optional: true, nullable: true,
|
optional: true, nullable: true,
|
||||||
},
|
},
|
||||||
smtpPort: {
|
smtpPort: {
|
||||||
type: 'string',
|
type: 'number',
|
||||||
optional: true, nullable: true,
|
optional: true, nullable: true,
|
||||||
},
|
},
|
||||||
smtpUser: {
|
smtpUser: {
|
||||||
|
@ -299,6 +231,10 @@ export const meta = {
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
optional: true, nullable: false,
|
optional: true, nullable: false,
|
||||||
},
|
},
|
||||||
|
policies: {
|
||||||
|
type: 'object',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
} as const;
|
} as const;
|
||||||
|
@ -349,7 +285,6 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
|
||||||
iconUrl: instance.iconUrl,
|
iconUrl: instance.iconUrl,
|
||||||
backgroundImageUrl: instance.backgroundImageUrl,
|
backgroundImageUrl: instance.backgroundImageUrl,
|
||||||
logoImageUrl: instance.logoImageUrl,
|
logoImageUrl: instance.logoImageUrl,
|
||||||
maxNoteTextLength: MAX_NOTE_TEXT_LENGTH, // 後方互換性のため
|
|
||||||
defaultLightTheme: instance.defaultLightTheme,
|
defaultLightTheme: instance.defaultLightTheme,
|
||||||
defaultDarkTheme: instance.defaultDarkTheme,
|
defaultDarkTheme: instance.defaultDarkTheme,
|
||||||
enableEmail: instance.enableEmail,
|
enableEmail: instance.enableEmail,
|
||||||
|
|
|
@ -82,11 +82,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
emojis: await this.emojiEntityService.packMany(emojis, {
|
emojis: await this.emojiEntityService.packSimpleMany(emojis),
|
||||||
omitId: true,
|
|
||||||
omitHost: true,
|
|
||||||
withUrl: true,
|
|
||||||
}),
|
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -295,7 +295,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
|
||||||
iconUrl: instance.iconUrl,
|
iconUrl: instance.iconUrl,
|
||||||
backgroundImageUrl: instance.backgroundImageUrl,
|
backgroundImageUrl: instance.backgroundImageUrl,
|
||||||
logoImageUrl: instance.logoImageUrl,
|
logoImageUrl: instance.logoImageUrl,
|
||||||
maxNoteTextLength: MAX_NOTE_TEXT_LENGTH, // 後方互換性のため
|
maxNoteTextLength: MAX_NOTE_TEXT_LENGTH,
|
||||||
defaultLightTheme: instance.defaultLightTheme,
|
defaultLightTheme: instance.defaultLightTheme,
|
||||||
defaultDarkTheme: instance.defaultDarkTheme,
|
defaultDarkTheme: instance.defaultDarkTheme,
|
||||||
ads: ads.map(ad => ({
|
ads: ads.map(ad => ({
|
||||||
|
|
|
@ -42,10 +42,10 @@ export interface InternalStreamTypes {
|
||||||
|
|
||||||
export interface BroadcastTypes {
|
export interface BroadcastTypes {
|
||||||
emojiAdded: {
|
emojiAdded: {
|
||||||
emoji: Packed<'Emoji'>;
|
emoji: Packed<'EmojiDetailed'>;
|
||||||
};
|
};
|
||||||
emojiUpdated: {
|
emojiUpdated: {
|
||||||
emojis: Packed<'Emoji'>[];
|
emojis: Packed<'EmojiDetailed'>[];
|
||||||
};
|
};
|
||||||
emojiDeleted: {
|
emojiDeleted: {
|
||||||
emojis: {
|
emojis: {
|
||||||
|
|
|
@ -1,5 +1,16 @@
|
||||||
import Resolver from '../../src/activitypub/resolver.js';
|
import type { Config } from '@/config.js';
|
||||||
import { IObject } from '../../src/activitypub/type.js';
|
import type { ApDbResolverService } from '@/core/activitypub/ApDbResolverService.js';
|
||||||
|
import type { ApRendererService } from '@/core/activitypub/ApRendererService.js';
|
||||||
|
import type { ApRequestService } from '@/core/activitypub/ApRequestService.js';
|
||||||
|
import { Resolver } from '@/core/activitypub/ApResolverService.js';
|
||||||
|
import type { IObject } from '@/core/activitypub/type.js';
|
||||||
|
import type { HttpRequestService } from '@/core/HttpRequestService.js';
|
||||||
|
import type { InstanceActorService } from '@/core/InstanceActorService.js';
|
||||||
|
import type { LoggerService } from '@/core/LoggerService.js';
|
||||||
|
import type { MetaService } from '@/core/MetaService.js';
|
||||||
|
import type { UtilityService } from '@/core/UtilityService.js';
|
||||||
|
import { bindThis } from '@/decorators.js';
|
||||||
|
import type { NoteReactionsRepository, NotesRepository, PollsRepository, UsersRepository } from '@/models/index.js';
|
||||||
|
|
||||||
type MockResponse = {
|
type MockResponse = {
|
||||||
type: string;
|
type: string;
|
||||||
|
@ -8,6 +19,25 @@ type MockResponse = {
|
||||||
|
|
||||||
export class MockResolver extends Resolver {
|
export class MockResolver extends Resolver {
|
||||||
private _rs = new Map<string, MockResponse>();
|
private _rs = new Map<string, MockResponse>();
|
||||||
|
|
||||||
|
constructor(loggerService: LoggerService) {
|
||||||
|
super(
|
||||||
|
{} as Config,
|
||||||
|
{} as UsersRepository,
|
||||||
|
{} as NotesRepository,
|
||||||
|
{} as PollsRepository,
|
||||||
|
{} as NoteReactionsRepository,
|
||||||
|
{} as UtilityService,
|
||||||
|
{} as InstanceActorService,
|
||||||
|
{} as MetaService,
|
||||||
|
{} as ApRequestService,
|
||||||
|
{} as HttpRequestService,
|
||||||
|
{} as ApRendererService,
|
||||||
|
{} as ApDbResolverService,
|
||||||
|
loggerService,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
public async _register(uri: string, content: string | Record<string, any>, type = 'application/activity+json') {
|
public async _register(uri: string, content: string | Record<string, any>, type = 'application/activity+json') {
|
||||||
this._rs.set(uri, {
|
this._rs.set(uri, {
|
||||||
type,
|
type,
|
||||||
|
|
|
@ -2,8 +2,39 @@ process.env.NODE_ENV = 'test';
|
||||||
|
|
||||||
import * as assert from 'assert';
|
import * as assert from 'assert';
|
||||||
import rndstr from 'rndstr';
|
import rndstr from 'rndstr';
|
||||||
|
import { Test } from '@nestjs/testing';
|
||||||
|
import { jest } from '@jest/globals';
|
||||||
|
|
||||||
|
import { ApNoteService } from '@/core/activitypub/models/ApNoteService.js';
|
||||||
|
import { ApPersonService } from '@/core/activitypub/models/ApPersonService.js';
|
||||||
|
import { GlobalModule } from '@/GlobalModule.js';
|
||||||
|
import { CoreModule } from '@/core/CoreModule.js';
|
||||||
|
import { FederatedInstanceService } from '@/core/FederatedInstanceService.js';
|
||||||
|
import { LoggerService } from '@/core/LoggerService.js';
|
||||||
|
import { MockResolver } from '../misc/mock-resolver.js';
|
||||||
|
|
||||||
describe('ActivityPub', () => {
|
describe('ActivityPub', () => {
|
||||||
|
let noteService: ApNoteService;
|
||||||
|
let personService: ApPersonService;
|
||||||
|
let resolver: MockResolver;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
const app = await Test.createTestingModule({
|
||||||
|
imports: [GlobalModule, CoreModule],
|
||||||
|
}).compile();
|
||||||
|
|
||||||
|
await app.init();
|
||||||
|
app.enableShutdownHooks();
|
||||||
|
|
||||||
|
noteService = app.get<ApNoteService>(ApNoteService);
|
||||||
|
personService = app.get<ApPersonService>(ApPersonService);
|
||||||
|
resolver = new MockResolver(await app.resolve<LoggerService>(LoggerService));
|
||||||
|
|
||||||
|
// Prevent ApPersonService from fetching instance, as it causes Jest import-after-test error
|
||||||
|
const federatedInstanceService = app.get<FederatedInstanceService>(FederatedInstanceService);
|
||||||
|
jest.spyOn(federatedInstanceService, 'fetch').mockImplementation(() => new Promise(() => {}));
|
||||||
|
});
|
||||||
|
|
||||||
describe('Parse minimum object', () => {
|
describe('Parse minimum object', () => {
|
||||||
const host = 'https://host1.test';
|
const host = 'https://host1.test';
|
||||||
const preferredUsername = `${rndstr('A-Z', 4)}${rndstr('a-z', 4)}`;
|
const preferredUsername = `${rndstr('A-Z', 4)}${rndstr('a-z', 4)}`;
|
||||||
|
@ -28,13 +59,9 @@ describe('ActivityPub', () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
test('Minimum Actor', async () => {
|
test('Minimum Actor', async () => {
|
||||||
const { MockResolver } = await import('../misc/mock-resolver.js');
|
|
||||||
const { createPerson } = await import('../../src/activitypub/models/person.js');
|
|
||||||
|
|
||||||
const resolver = new MockResolver();
|
|
||||||
resolver._register(actor.id, actor);
|
resolver._register(actor.id, actor);
|
||||||
|
|
||||||
const user = await createPerson(actor.id, resolver);
|
const user = await personService.createPerson(actor.id, resolver);
|
||||||
|
|
||||||
assert.deepStrictEqual(user.uri, actor.id);
|
assert.deepStrictEqual(user.uri, actor.id);
|
||||||
assert.deepStrictEqual(user.username, actor.preferredUsername);
|
assert.deepStrictEqual(user.username, actor.preferredUsername);
|
||||||
|
@ -42,14 +69,10 @@ describe('ActivityPub', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Minimum Note', async () => {
|
test('Minimum Note', async () => {
|
||||||
const { MockResolver } = await import('../misc/mock-resolver.js');
|
|
||||||
const { createNote } = await import('../../src/activitypub/models/note.js');
|
|
||||||
|
|
||||||
const resolver = new MockResolver();
|
|
||||||
resolver._register(actor.id, actor);
|
resolver._register(actor.id, actor);
|
||||||
resolver._register(post.id, post);
|
resolver._register(post.id, post);
|
||||||
|
|
||||||
const note = await createNote(post.id, resolver, true);
|
const note = await noteService.createNote(post.id, resolver, true);
|
||||||
|
|
||||||
assert.deepStrictEqual(note?.uri, post.id);
|
assert.deepStrictEqual(note?.uri, post.id);
|
||||||
assert.deepStrictEqual(note.visibility, 'public');
|
assert.deepStrictEqual(note.visibility, 'public');
|
||||||
|
@ -75,13 +98,9 @@ describe('ActivityPub', () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
test('Actor', async () => {
|
test('Actor', async () => {
|
||||||
const { MockResolver } = await import('../misc/mock-resolver.js');
|
|
||||||
const { createPerson } = await import('../../src/activitypub/models/person.js');
|
|
||||||
|
|
||||||
const resolver = new MockResolver();
|
|
||||||
resolver._register(actor.id, actor);
|
resolver._register(actor.id, actor);
|
||||||
|
|
||||||
const user = await createPerson(actor.id, resolver);
|
const user = await personService.createPerson(actor.id, resolver);
|
||||||
|
|
||||||
assert.deepStrictEqual(user.name, actor.name.substr(0, 128));
|
assert.deepStrictEqual(user.name, actor.name.substr(0, 128));
|
||||||
});
|
});
|
|
@ -10,6 +10,12 @@ import { getAccountFromId } from '@/scripts/get-account-from-id';
|
||||||
import { char2fileName } from '@/scripts/twemoji-base';
|
import { char2fileName } from '@/scripts/twemoji-base';
|
||||||
import * as url from '@/scripts/url';
|
import * as url from '@/scripts/url';
|
||||||
|
|
||||||
|
const closeNotificationsByTags = async (tags: string[]) => {
|
||||||
|
for (const n of (await Promise.all(tags.map(tag => globalThis.registration.getNotifications({ tag })))).flat()) {
|
||||||
|
n.close();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const iconUrl = (name: badgeNames) => `/static-assets/tabler-badges/${name}.png`;
|
const iconUrl = (name: badgeNames) => `/static-assets/tabler-badges/${name}.png`;
|
||||||
/* How to add a new badge:
|
/* How to add a new badge:
|
||||||
* 1. Find the icon and download png from https://tabler-icons.io/
|
* 1. Find the icon and download png from https://tabler-icons.io/
|
||||||
|
@ -23,7 +29,7 @@ export async function createNotification<K extends keyof pushNotificationDataMap
|
||||||
const n = await composeNotification(data);
|
const n = await composeNotification(data);
|
||||||
|
|
||||||
if (n) {
|
if (n) {
|
||||||
return self.registration.showNotification(...n);
|
return globalThis.registration.showNotification(...n);
|
||||||
} else {
|
} else {
|
||||||
console.error('Could not compose notification', data);
|
console.error('Could not compose notification', data);
|
||||||
return createEmptyNotification();
|
return createEmptyNotification();
|
||||||
|
@ -161,6 +167,7 @@ async function composeNotification(data: pushNotificationDataMap[keyof pushNotif
|
||||||
badge = iconUrl('plus');
|
badge = iconUrl('plus');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const tag = `reaction:${data.body.note.id}`;
|
||||||
return [`${reaction} ${getUserName(data.body.user)}`, {
|
return [`${reaction} ${getUserName(data.body.user)}`, {
|
||||||
body: data.body.note.text ?? '',
|
body: data.body.note.text ?? '',
|
||||||
icon: data.body.user.avatarUrl,
|
icon: data.body.user.avatarUrl,
|
||||||
|
@ -239,7 +246,7 @@ export async function createEmptyNotification() {
|
||||||
const i18n = await swLang.i18n as I18n<any>;
|
const i18n = await swLang.i18n as I18n<any>;
|
||||||
const { t } = i18n;
|
const { t } = i18n;
|
||||||
|
|
||||||
await self.registration.showNotification(
|
await globalThis.registration.showNotification(
|
||||||
t('_notification.emptyPushNotificationMessage'),
|
t('_notification.emptyPushNotificationMessage'),
|
||||||
{
|
{
|
||||||
silent: true,
|
silent: true,
|
||||||
|
@ -248,16 +255,11 @@ export async function createEmptyNotification() {
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
res();
|
|
||||||
|
|
||||||
setTimeout(async () => {
|
setTimeout(async () => {
|
||||||
for (const n of
|
try {
|
||||||
[
|
await closeNotificationsByTags(['user_visible_auto_notification', 'read_notification']);
|
||||||
...(await self.registration.getNotifications({ tag: 'user_visible_auto_notification' })),
|
} finally {
|
||||||
...(await self.registration.getNotifications({ tag: 'read_notification' })),
|
res();
|
||||||
]
|
|
||||||
) {
|
|
||||||
n.close();
|
|
||||||
}
|
}
|
||||||
}, 1000);
|
}, 1000);
|
||||||
});
|
});
|
||||||
|
|
|
@ -51,11 +51,11 @@ export async function openClient(order: swMessageOrderType, url: string, loginId
|
||||||
return client;
|
return client;
|
||||||
}
|
}
|
||||||
|
|
||||||
return self.clients.openWindow(getUrlWithLoginId(url, loginId));
|
return globalThis.clients.openWindow(getUrlWithLoginId(url, loginId));
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function findClient() {
|
export async function findClient() {
|
||||||
const clients = await self.clients.matchAll({
|
const clients = await globalThis.clients.matchAll({
|
||||||
type: 'window',
|
type: 'window',
|
||||||
});
|
});
|
||||||
for (const c of clients) {
|
for (const c of clients) {
|
||||||
|
|
|
@ -6,7 +6,7 @@ import * as swos from '@/scripts/operations';
|
||||||
import { acct as getAcct } from '@/filters/user';
|
import { acct as getAcct } from '@/filters/user';
|
||||||
|
|
||||||
globalThis.addEventListener('install', ev => {
|
globalThis.addEventListener('install', ev => {
|
||||||
//ev.waitUntil(self.skipWaiting());
|
//ev.waitUntil(globalThis.skipWaiting());
|
||||||
});
|
});
|
||||||
|
|
||||||
globalThis.addEventListener('activate', ev => {
|
globalThis.addEventListener('activate', ev => {
|
||||||
|
@ -17,7 +17,7 @@ globalThis.addEventListener('activate', ev => {
|
||||||
.filter((v) => v !== swLang.cacheName)
|
.filter((v) => v !== swLang.cacheName)
|
||||||
.map(name => caches.delete(name)),
|
.map(name => caches.delete(name)),
|
||||||
))
|
))
|
||||||
.then(() => self.clients.claim()),
|
.then(() => globalThis.clients.claim()),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -40,7 +40,7 @@ globalThis.addEventListener('fetch', ev => {
|
||||||
|
|
||||||
globalThis.addEventListener('push', ev => {
|
globalThis.addEventListener('push', ev => {
|
||||||
// クライアント取得
|
// クライアント取得
|
||||||
ev.waitUntil(self.clients.matchAll({
|
ev.waitUntil(globalThis.clients.matchAll({
|
||||||
includeUncontrolled: true,
|
includeUncontrolled: true,
|
||||||
type: 'window',
|
type: 'window',
|
||||||
}).then(async (clients: readonly WindowClient[]) => {
|
}).then(async (clients: readonly WindowClient[]) => {
|
||||||
|
@ -53,29 +53,26 @@ globalThis.addEventListener('push', ev => {
|
||||||
// 1日以上経過している場合は無視
|
// 1日以上経過している場合は無視
|
||||||
if ((new Date()).getTime() - data.dateTime > 1000 * 60 * 60 * 24) break;
|
if ((new Date()).getTime() - data.dateTime > 1000 * 60 * 60 * 24) break;
|
||||||
|
|
||||||
// クライアントがあったらストリームに接続しているということなので通知しない
|
|
||||||
if (clients.length !== 0) break;
|
|
||||||
|
|
||||||
return createNotification(data);
|
return createNotification(data);
|
||||||
case 'readAllNotifications':
|
case 'readAllNotifications':
|
||||||
for (const n of await self.registration.getNotifications()) {
|
for (const n of await globalThis.registration.getNotifications()) {
|
||||||
if (n?.data?.type === 'notification') n.close();
|
if (n?.data?.type === 'notification') n.close();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'readAllAntennas':
|
case 'readAllAntennas':
|
||||||
for (const n of await self.registration.getNotifications()) {
|
for (const n of await globalThis.registration.getNotifications()) {
|
||||||
if (n?.data?.type === 'unreadAntennaNote') n.close();
|
if (n?.data?.type === 'unreadAntennaNote') n.close();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'readNotifications':
|
case 'readNotifications':
|
||||||
for (const n of await self.registration.getNotifications()) {
|
for (const n of await globalThis.registration.getNotifications()) {
|
||||||
if (data.body.notificationIds.includes(n.data.body.id)) {
|
if (data.body.notificationIds.includes(n.data.body.id)) {
|
||||||
n.close();
|
n.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'readAntenna':
|
case 'readAntenna':
|
||||||
for (const n of await self.registration.getNotifications()) {
|
for (const n of await globalThis.registration.getNotifications()) {
|
||||||
if (n?.data?.type === 'unreadAntennaNote' && data.body.antennaId === n.data.body.antenna.id) {
|
if (n?.data?.type === 'unreadAntennaNote' && data.body.antennaId === n.data.body.antenna.id) {
|
||||||
n.close();
|
n.close();
|
||||||
}
|
}
|
||||||
|
@ -83,7 +80,8 @@ globalThis.addEventListener('push', ev => {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return createEmptyNotification();
|
await createEmptyNotification();
|
||||||
|
return;
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue