merge: upstream

This commit is contained in:
Marie 2024-02-23 13:42:52 +01:00
commit 15d2319011
No known key found for this signature in database
GPG key ID: 56569BBE47D2C828
92 changed files with 1677 additions and 1086 deletions

View file

@ -25,6 +25,7 @@ export const paramDef = {
host: { type: 'string' },
isSuspended: { type: 'boolean' },
isNSFW: { type: 'boolean' },
moderationNote: { type: 'string' },
},
required: ['host'],
} as const;
@ -46,29 +47,32 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
throw new Error('instance not found');
}
if (ps.isSuspended != null) {
await this.federatedInstanceService.update(instance.id, {
isSuspended: ps.isSuspended,
});
await this.federatedInstanceService.update(instance.id, {
isSuspended: ps.isSuspended,
isNSFW: ps.isNSFW,
moderationNote: ps.moderationNote,
});
if (instance.isSuspended !== ps.isSuspended) {
if (ps.isSuspended) {
this.moderationLogService.log(me, 'suspendRemoteInstance', {
id: instance.id,
host: instance.host,
});
} else {
this.moderationLogService.log(me, 'unsuspendRemoteInstance', {
id: instance.id,
host: instance.host,
});
}
if (ps.isSuspended != null && instance.isSuspended !== ps.isSuspended) {
if (ps.isSuspended) {
this.moderationLogService.log(me, 'suspendRemoteInstance', {
id: instance.id,
host: instance.host,
});
} else {
this.moderationLogService.log(me, 'unsuspendRemoteInstance', {
id: instance.id,
host: instance.host,
});
}
}
if (ps.isNSFW != null) {
await this.federatedInstanceService.update(instance.id, {
isNSFW: ps.isNSFW,
if (ps.moderationNote != null && instance.moderationNote !== ps.moderationNote) {
this.moderationLogService.log(me, 'updateRemoteInstanceNote', {
id: instance.id,
host: instance.host,
before: instance.moderationNote,
after: ps.moderationNote,
});
}
});

View file

@ -124,9 +124,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
notes.sort((a, b) => a.id > b.id ? -1 : 1);
}
if (notes.length > 0) {
this.noteReadService.read(me.id, notes);
}
this.noteReadService.read(me.id, notes);
return await this.noteEntityService.packMany(notes, me);
});

View file

@ -43,7 +43,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
const instance = await this.instancesRepository
.findOneBy({ host: this.utilityService.toPuny(ps.host) });
return instance ? await this.instanceEntityService.pack(instance) : null;
return instance ? await this.instanceEntityService.pack(instance, me) : null;
});
}
}

View file

@ -51,7 +51,7 @@ export const paramDef = {
} },
visibility: { type: 'string', enum: ['public', 'private'] },
},
required: ['flashId', 'title', 'summary', 'script', 'permissions'],
required: ['flashId'],
} as const;
@Injectable()
@ -71,11 +71,11 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
await this.flashsRepository.update(flash.id, {
updatedAt: new Date(),
title: ps.title,
summary: ps.summary,
script: ps.script,
permissions: ps.permissions,
visibility: ps.visibility,
...Object.fromEntries(
Object.entries(ps).filter(
([key, val]) => (key !== 'flashId') && Object.hasOwn(paramDef.properties, key)
)
),
});
});
}

View file

@ -100,22 +100,11 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
throw err;
});
// Check if already following
const exist = await this.followingsRepository.exists({
where: {
followerId: follower.id,
followeeId: followee.id,
},
});
if (exist) {
throw new ApiError(meta.errors.alreadyFollowing);
}
try {
await this.userFollowingService.follow(follower, followee, { withReplies: ps.withReplies });
} catch (e) {
if (e instanceof IdentifiableError) {
if (e.id === 'ec3f65c0-a9d1-47d9-8791-b2e7b9dcdced') throw new ApiError(meta.errors.alreadyFollowing);
if (e.id === '710e8fb0-b8c3-4922-be49-d5d93d8e6a6e') throw new ApiError(meta.errors.blocking);
if (e.id === '3338392a-f764-498d-8855-db939dcf8c48') throw new ApiError(meta.errors.blocked);
}

View file

@ -12,6 +12,7 @@ import type { MiDriveFile } from '@/models/DriveFile.js';
import { IdService } from '@/core/IdService.js';
import { GalleryPostEntityService } from '@/core/entities/GalleryPostEntityService.js';
import { DI } from '@/di-symbols.js';
import { isNotNull } from '@/misc/is-not-null.js';
export const meta = {
tags: ['gallery'],
@ -69,7 +70,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
id: fileId,
userId: me.id,
}),
))).filter((file): file is MiDriveFile => file != null);
))).filter(isNotNull);
if (files.length === 0) {
throw new Error();

View file

@ -10,6 +10,7 @@ import type { DriveFilesRepository, GalleryPostsRepository } from '@/models/_.js
import type { MiDriveFile } from '@/models/DriveFile.js';
import { GalleryPostEntityService } from '@/core/entities/GalleryPostEntityService.js';
import { DI } from '@/di-symbols.js';
import { isNotNull } from '@/misc/is-not-null.js';
export const meta = {
tags: ['gallery'],
@ -67,7 +68,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
id: fileId,
userId: me.id,
}),
))).filter((file): file is MiDriveFile => file != null);
))).filter(isNotNull);
if (files.length === 0) {
throw new Error();

View file

@ -43,7 +43,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
super(meta, paramDef, async (ps, me) => {
const hashtags = await this.hashtagsRepository.createQueryBuilder('tag')
.where('tag.name like :q', { q: sqlLikeEscape(ps.query.toLowerCase()) + '%' })
.orderBy('tag.count', 'DESC')
.orderBy('tag.mentionedLocalUsersCount', 'DESC')
.groupBy('tag.id')
.limit(ps.limit)
.offset(ps.offset)

View file

@ -3,17 +3,9 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { IsNull, LessThanOrEqual, MoreThan, Brackets } from 'typeorm';
import { Inject, Injectable } from '@nestjs/common';
import JSON5 from 'json5';
import type { AdsRepository } from '@/models/_.js';
import { Injectable } from '@nestjs/common';
import { Endpoint } from '@/server/api/endpoint-base.js';
import { UserEntityService } from '@/core/entities/UserEntityService.js';
import { MetaService } from '@/core/MetaService.js';
import { InstanceActorService } from '@/core/InstanceActorService.js';
import type { Config } from '@/config.js';
import { DI } from '@/di-symbols.js';
import { DEFAULT_POLICIES } from '@/core/RoleService.js';
import { MetaEntityService } from '@/core/entities/MetaEntityService.js';
export const meta = {
tags: ['meta'],
@ -22,309 +14,10 @@ export const meta = {
res: {
type: 'object',
optional: false, nullable: false,
properties: {
maintainerName: {
type: 'string',
optional: false, nullable: true,
},
maintainerEmail: {
type: 'string',
optional: false, nullable: true,
},
version: {
type: 'string',
optional: false, nullable: false,
},
providesTarball: {
type: 'boolean',
optional: false, nullable: false,
},
name: {
type: 'string',
optional: false, nullable: false,
},
shortName: {
type: 'string',
optional: false, nullable: true,
},
uri: {
type: 'string',
optional: false, nullable: false,
format: 'url',
example: 'https://misskey.example.com',
},
description: {
type: 'string',
optional: false, nullable: true,
},
langs: {
type: 'array',
optional: false, nullable: false,
items: {
type: 'string',
optional: false, nullable: false,
},
},
tosUrl: {
type: 'string',
optional: false, nullable: true,
},
repositoryUrl: {
type: 'string',
optional: false, nullable: true,
default: 'https://github.com/misskey-dev/misskey',
},
feedbackUrl: {
type: 'string',
optional: false, nullable: true,
default: 'https://github.com/misskey-dev/misskey/issues/new',
},
defaultDarkTheme: {
type: 'string',
optional: false, nullable: true,
},
defaultLightTheme: {
type: 'string',
optional: false, nullable: true,
},
defaultLike: {
type: 'string',
optional: false, nullable: true,
},
disableRegistration: {
type: 'boolean',
optional: false, nullable: false,
},
cacheRemoteFiles: {
type: 'boolean',
optional: false, nullable: false,
},
cacheRemoteSensitiveFiles: {
type: 'boolean',
optional: false, nullable: false,
},
emailRequiredForSignup: {
type: 'boolean',
optional: false, nullable: false,
},
approvalRequiredForSignup: {
type: 'boolean',
optional: false, nullable: false,
},
enableHcaptcha: {
type: 'boolean',
optional: false, nullable: false,
},
hcaptchaSiteKey: {
type: 'string',
optional: false, nullable: true,
},
enableMcaptcha: {
type: 'boolean',
optional: false, nullable: false,
},
mcaptchaSiteKey: {
type: 'string',
optional: false, nullable: true,
},
mcaptchaInstanceUrl: {
type: 'string',
optional: false, nullable: true,
},
enableRecaptcha: {
type: 'boolean',
optional: false, nullable: false,
},
recaptchaSiteKey: {
type: 'string',
optional: false, nullable: true,
},
enableTurnstile: {
type: 'boolean',
optional: false, nullable: false,
},
turnstileSiteKey: {
type: 'string',
optional: false, nullable: true,
},
swPublickey: {
type: 'string',
optional: false, nullable: true,
},
mascotImageUrl: {
type: 'string',
optional: false, nullable: false,
default: '/assets/ai.png',
},
bannerUrl: {
type: 'string',
optional: false, nullable: false,
},
serverErrorImageUrl: {
type: 'string',
optional: false, nullable: true,
},
infoImageUrl: {
type: 'string',
optional: false, nullable: true,
},
notFoundImageUrl: {
type: 'string',
optional: false, nullable: true,
},
iconUrl: {
type: 'string',
optional: false, nullable: true,
},
maxNoteTextLength: {
type: 'number',
optional: false, nullable: false,
},
ads: {
type: 'array',
optional: false, nullable: false,
items: {
type: 'object',
optional: false, nullable: false,
properties: {
id: {
type: 'string',
optional: false, nullable: false,
format: 'id',
example: 'xxxxxxxxxx',
},
url: {
type: 'string',
optional: false, nullable: false,
format: 'url',
},
place: {
type: 'string',
optional: false, nullable: false,
},
ratio: {
type: 'number',
optional: false, nullable: false,
},
imageUrl: {
type: 'string',
optional: false, nullable: false,
format: 'url',
},
dayOfWeek: {
type: 'integer',
optional: false, nullable: false,
},
},
},
},
notesPerOneAd: {
type: 'number',
optional: false, nullable: false,
default: 0,
},
requireSetup: {
type: 'boolean',
optional: false, nullable: false,
example: false,
},
enableEmail: {
type: 'boolean',
optional: false, nullable: false,
},
enableServiceWorker: {
type: 'boolean',
optional: false, nullable: false,
},
translatorAvailable: {
type: 'boolean',
optional: false, nullable: false,
},
proxyAccountName: {
type: 'string',
optional: false, nullable: true,
},
mediaProxy: {
type: 'string',
optional: false, nullable: false,
},
features: {
type: 'object',
optional: true, nullable: false,
properties: {
registration: {
type: 'boolean',
optional: false, nullable: false,
},
localTimeline: {
type: 'boolean',
optional: false, nullable: false,
},
globalTimeline: {
type: 'boolean',
optional: false, nullable: false,
},
hcaptcha: {
type: 'boolean',
optional: false, nullable: false,
},
recaptcha: {
type: 'boolean',
optional: false, nullable: false,
},
objectStorage: {
type: 'boolean',
optional: false, nullable: false,
},
serviceWorker: {
type: 'boolean',
optional: false, nullable: false,
},
miauth: {
type: 'boolean',
optional: true, nullable: false,
default: true,
},
},
},
backgroundImageUrl: {
type: 'string',
optional: false, nullable: true,
},
impressumUrl: {
type: 'string',
optional: false, nullable: true,
},
donationUrl: {
type: 'string',
optional: false, nullable: true,
},
logoImageUrl: {
type: 'string',
optional: false, nullable: true,
},
privacyPolicyUrl: {
type: 'string',
optional: false, nullable: true,
},
serverRules: {
type: 'array',
optional: false, nullable: false,
items: {
type: 'string',
},
},
themeColor: {
type: 'string',
optional: false, nullable: true,
},
policies: {
type: 'object',
optional: false, nullable: false,
ref: 'RolePolicies',
},
},
oneOf: [
{ type: 'object', ref: 'MetaLite' },
{ type: 'object', ref: 'MetaDetailed' },
],
},
} as const;
@ -339,119 +32,10 @@ export const paramDef = {
@Injectable()
export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
constructor(
@Inject(DI.config)
private config: Config,
@Inject(DI.adsRepository)
private adsRepository: AdsRepository,
private userEntityService: UserEntityService,
private metaService: MetaService,
private instanceActorService: InstanceActorService,
private metaEntityService: MetaEntityService,
) {
super(meta, paramDef, async (ps, me) => {
const instance = await this.metaService.fetch(true);
const ads = await this.adsRepository.createQueryBuilder('ads')
.where('ads.expiresAt > :now', { now: new Date() })
.andWhere('ads.startsAt <= :now', { now: new Date() })
.andWhere(new Brackets(qb => {
// 曜日のビットフラグを確認する
qb.where('ads.dayOfWeek & :dayOfWeek > 0', { dayOfWeek: 1 << new Date().getDay() })
.orWhere('ads.dayOfWeek = 0');
}))
.getMany();
const response: any = {
maintainerName: instance.maintainerName,
maintainerEmail: instance.maintainerEmail,
version: this.config.version,
providesTarball: this.config.publishTarballInsteadOfProvideRepositoryUrl,
name: instance.name,
shortName: instance.shortName,
uri: this.config.url,
description: instance.description,
langs: instance.langs,
tosUrl: instance.termsOfServiceUrl,
repositoryUrl: instance.repositoryUrl,
feedbackUrl: instance.feedbackUrl,
impressumUrl: instance.impressumUrl,
donationUrl: instance.donationUrl,
privacyPolicyUrl: instance.privacyPolicyUrl,
disableRegistration: instance.disableRegistration,
emailRequiredForSignup: instance.emailRequiredForSignup,
approvalRequiredForSignup: instance.approvalRequiredForSignup,
enableHcaptcha: instance.enableHcaptcha,
hcaptchaSiteKey: instance.hcaptchaSiteKey,
enableMcaptcha: instance.enableMcaptcha,
mcaptchaSiteKey: instance.mcaptchaSitekey,
mcaptchaInstanceUrl: instance.mcaptchaInstanceUrl,
enableRecaptcha: instance.enableRecaptcha,
enableAchievements: instance.enableAchievements,
recaptchaSiteKey: instance.recaptchaSiteKey,
enableTurnstile: instance.enableTurnstile,
turnstileSiteKey: instance.turnstileSiteKey,
swPublickey: instance.swPublicKey,
themeColor: instance.themeColor,
mascotImageUrl: instance.mascotImageUrl,
bannerUrl: instance.bannerUrl,
infoImageUrl: instance.infoImageUrl,
serverErrorImageUrl: instance.serverErrorImageUrl,
notFoundImageUrl: instance.notFoundImageUrl,
iconUrl: instance.iconUrl,
backgroundImageUrl: instance.backgroundImageUrl,
logoImageUrl: instance.logoImageUrl,
maxNoteTextLength: this.config.maxNoteLength,
// クライアントの手間を減らすためあらかじめJSONに変換しておく
defaultLightTheme: instance.defaultLightTheme ? JSON.stringify(JSON5.parse(instance.defaultLightTheme)) : null,
defaultDarkTheme: instance.defaultDarkTheme ? JSON.stringify(JSON5.parse(instance.defaultDarkTheme)) : null,
defaultLike: instance.defaultLike,
ads: ads.map(ad => ({
id: ad.id,
url: ad.url,
place: ad.place,
ratio: ad.ratio,
imageUrl: ad.imageUrl,
dayOfWeek: ad.dayOfWeek,
})),
notesPerOneAd: instance.notesPerOneAd,
enableEmail: instance.enableEmail,
enableServiceWorker: instance.enableServiceWorker,
translatorAvailable: instance.deeplAuthKey != null || instance.deeplFreeMode && instance.deeplFreeInstance,
serverRules: instance.serverRules,
policies: { ...DEFAULT_POLICIES, ...instance.policies },
mediaProxy: this.config.mediaProxy,
...(ps.detail ? {
cacheRemoteFiles: instance.cacheRemoteFiles,
cacheRemoteSensitiveFiles: instance.cacheRemoteSensitiveFiles,
requireSetup: !await this.instanceActorService.realLocalUsersPresent(),
} : {}),
};
if (ps.detail) {
const proxyAccount = instance.proxyAccountId ? await this.userEntityService.pack(instance.proxyAccountId).catch(() => null) : null;
response.proxyAccountName = proxyAccount ? proxyAccount.username : null;
response.features = {
registration: !instance.disableRegistration,
emailRequiredForSignup: instance.emailRequiredForSignup,
hcaptcha: instance.enableHcaptcha,
recaptcha: instance.enableRecaptcha,
turnstile: instance.enableTurnstile,
objectStorage: instance.useObjectStorage,
serviceWorker: instance.enableServiceWorker,
miauth: true,
};
}
return response;
return ps.detail ? await this.metaEntityService.packDetailed() : await this.metaEntityService.pack();
});
}
}

View file

@ -19,6 +19,7 @@ import { DI } from '@/di-symbols.js';
import { isPureRenote } from '@/misc/is-pure-renote.js';
import { MetaService } from '@/core/MetaService.js';
import { UtilityService } from '@/core/UtilityService.js';
import { IdentifiableError } from '@/misc/identifiable-error.js';
import { ApiError } from '../../error.js';
export const meta = {
@ -387,8 +388,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
};
} catch (e) {
// TODO: 他のErrorもここでキャッチしてエラーメッセージを当てるようにしたい
if (e instanceof NoteCreateService.ContainsProhibitedWordsError) {
throw new ApiError(meta.errors.containsProhibitedWords);
if (e instanceof IdentifiableError) {
if (e.id === '689ee33f-f97c-479a-ac49-1b9f8140af99') throw new ApiError(meta.errors.containsProhibitedWords);
}
throw e;

View file

@ -12,6 +12,7 @@ import { Endpoint } from '@/server/api/endpoint-base.js';
import { MetaService } from '@/core/MetaService.js';
import { UserEntityService } from '@/core/entities/UserEntityService.js';
import { DI } from '@/di-symbols.js';
import { isNotNull } from '@/misc/is-not-null.js';
export const meta = {
tags: ['users'],
@ -52,7 +53,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
host: acct.host ?? IsNull(),
})));
return await this.userEntityService.packMany(users.filter(x => x !== null) as MiUser[], me, { schema: 'UserDetailed' });
return await this.userEntityService.packMany(users.filter(isNotNull), me, { schema: 'UserDetailed' });
});
}
}

View file

@ -9,6 +9,7 @@ import type { SwSubscriptionsRepository } from '@/models/_.js';
import { Endpoint } from '@/server/api/endpoint-base.js';
import { MetaService } from '@/core/MetaService.js';
import { DI } from '@/di-symbols.js';
import { PushNotificationService } from '@/core/PushNotificationService.js';
export const meta = {
tags: ['account'],
@ -66,6 +67,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
private idService: IdService,
private metaService: MetaService,
private pushNotificationService: PushNotificationService,
) {
super(meta, paramDef, async (ps, me) => {
// if already subscribed
@ -97,6 +99,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
sendReadMessage: ps.sendReadMessage,
});
this.pushNotificationService.refreshCache(me.id);
return {
state: 'subscribed' as const,
key: instance.swPublicKey,

View file

@ -7,6 +7,7 @@ import { Inject, Injectable } from '@nestjs/common';
import type { SwSubscriptionsRepository } from '@/models/_.js';
import { Endpoint } from '@/server/api/endpoint-base.js';
import { DI } from '@/di-symbols.js';
import { PushNotificationService } from '@/core/PushNotificationService.js';
export const meta = {
tags: ['account'],
@ -29,12 +30,18 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
constructor(
@Inject(DI.swSubscriptionsRepository)
private swSubscriptionsRepository: SwSubscriptionsRepository,
private pushNotificationService: PushNotificationService,
) {
super(meta, paramDef, async (ps, me) => {
await this.swSubscriptionsRepository.delete({
...(me ? { userId: me.id } : {}),
endpoint: ps.endpoint,
});
if (me) {
this.pushNotificationService.refreshCache(me.id);
}
});
}
}

View file

@ -7,6 +7,7 @@ import { Inject, Injectable } from '@nestjs/common';
import type { SwSubscriptionsRepository } from '@/models/_.js';
import { Endpoint } from '@/server/api/endpoint-base.js';
import { DI } from '@/di-symbols.js';
import { PushNotificationService } from '@/core/PushNotificationService.js';
import { ApiError } from '../../error.js';
export const meta = {
@ -58,6 +59,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
constructor(
@Inject(DI.swSubscriptionsRepository)
private swSubscriptionsRepository: SwSubscriptionsRepository,
private pushNotificationService: PushNotificationService,
) {
super(meta, paramDef, async (ps, me) => {
const swSubscription = await this.swSubscriptionsRepository.findOneBy({
@ -77,6 +80,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
sendReadMessage: swSubscription.sendReadMessage,
});
this.pushNotificationService.refreshCache(me.id);
return {
userId: swSubscription.userId,
endpoint: swSubscription.endpoint,

View file

@ -19,6 +19,7 @@ import fastifyView from '@fastify/view';
import fastifyCookie from '@fastify/cookie';
import fastifyProxy from '@fastify/http-proxy';
import vary from 'vary';
import htmlSafeJsonStringify from 'htmlescape';
import type { Config } from '@/config.js';
import { getNoteSummary } from '@/misc/get-note-summary.js';
import { DI } from '@/di-symbols.js';
@ -28,12 +29,12 @@ import type { DbQueue, DeliverQueue, EndedPollNotificationQueue, InboxQueue, Obj
import { UserEntityService } from '@/core/entities/UserEntityService.js';
import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
import { PageEntityService } from '@/core/entities/PageEntityService.js';
import { MetaEntityService } from '@/core/entities/MetaEntityService.js';
import { GalleryPostEntityService } from '@/core/entities/GalleryPostEntityService.js';
import { ClipEntityService } from '@/core/entities/ClipEntityService.js';
import { ChannelEntityService } from '@/core/entities/ChannelEntityService.js';
import type { ChannelsRepository, ClipsRepository, FlashsRepository, GalleryPostsRepository, MiMeta, NotesRepository, PagesRepository, ReversiGamesRepository, UserProfilesRepository, UsersRepository } from '@/models/_.js';
import type Logger from '@/logger.js';
import { deepClone } from '@/misc/clone.js';
import { handleRequestRedirectToOmitSearch } from '@/misc/fastify-hook-handlers.js';
import { bindThis } from '@/decorators.js';
import { FlashEntityService } from '@/core/entities/FlashEntityService.js';
@ -93,6 +94,7 @@ export class ClientServerService {
private userEntityService: UserEntityService,
private noteEntityService: NoteEntityService,
private pageEntityService: PageEntityService,
private metaEntityService: MetaEntityService,
private galleryPostEntityService: GalleryPostEntityService,
private clipEntityService: ClipEntityService,
private channelEntityService: ChannelEntityService,
@ -175,7 +177,7 @@ export class ClientServerService {
}
@bindThis
private generateCommonPugData(meta: MiMeta) {
private async generateCommonPugData(meta: MiMeta) {
return {
instanceName: meta.name ?? 'Sharkey',
icon: meta.iconUrl,
@ -186,6 +188,8 @@ export class ClientServerService {
notFoundImageUrl: meta.notFoundImageUrl ?? 'https://launcher.moe/missingpage.webp',
instanceUrl: this.config.url,
randomMOTD: this.config.customMOTD ? this.config.customMOTD[Math.floor(Math.random() * this.config.customMOTD.length)] : undefined,
metaJson: htmlSafeJsonStringify(await this.metaEntityService.packDetailed(meta)),
now: Date.now(),
};
}
@ -451,7 +455,7 @@ export class ClientServerService {
url: this.config.url,
title: meta.name ?? 'Misskey',
desc: meta.description,
...this.generateCommonPugData(meta),
...await this.generateCommonPugData(meta),
});
};
@ -538,7 +542,7 @@ export class ClientServerService {
user, profile, me,
avatarUrl: user.avatarUrl ?? this.userEntityService.getIdenticonUrl(user),
sub: request.params.sub,
...this.generateCommonPugData(meta),
...await this.generateCommonPugData(meta),
});
} else {
// リモートユーザーなので
@ -588,7 +592,7 @@ export class ClientServerService {
avatarUrl: _note.user.avatarUrl,
// TODO: Let locale changeable by instance setting
summary: getNoteSummary(_note),
...this.generateCommonPugData(meta),
...await this.generateCommonPugData(meta),
});
} else {
return await renderBase(reply);
@ -627,7 +631,7 @@ export class ClientServerService {
page: _page,
profile,
avatarUrl: _page.user.avatarUrl,
...this.generateCommonPugData(meta),
...await this.generateCommonPugData(meta),
});
} else {
return await renderBase(reply);
@ -653,7 +657,7 @@ export class ClientServerService {
flash: _flash,
profile,
avatarUrl: _flash.user.avatarUrl,
...this.generateCommonPugData(meta),
...await this.generateCommonPugData(meta),
});
} else {
return await renderBase(reply);
@ -679,7 +683,7 @@ export class ClientServerService {
clip: _clip,
profile,
avatarUrl: _clip.user.avatarUrl,
...this.generateCommonPugData(meta),
...await this.generateCommonPugData(meta),
});
} else {
return await renderBase(reply);
@ -703,7 +707,7 @@ export class ClientServerService {
post: _post,
profile,
avatarUrl: _post.user.avatarUrl,
...this.generateCommonPugData(meta),
...await this.generateCommonPugData(meta),
});
} else {
return await renderBase(reply);
@ -722,7 +726,7 @@ export class ClientServerService {
reply.header('Cache-Control', 'public, max-age=15');
return await reply.view('channel', {
channel: _channel,
...this.generateCommonPugData(meta),
...await this.generateCommonPugData(meta),
});
} else {
return await renderBase(reply);
@ -741,7 +745,7 @@ export class ClientServerService {
reply.header('Cache-Control', 'public, max-age=3600');
return await reply.view('reversi-game', {
game: _game,
...this.generateCommonPugData(meta),
...await this.generateCommonPugData(meta),
});
} else {
return await renderBase(reply);

View file

@ -75,6 +75,9 @@ html
var CLIENT_ENTRY = "#{clientEntry.file}";
window.libopenmpt = window.Module;
script(type='application/json' id='misskey_meta' data-generated-at=now)
!= metaJson
script
include ../boot.js