Merge branch 'develop' into fix-10650

This commit is contained in:
かっこかり 2024-02-16 19:03:59 +09:00 committed by GitHub
commit 7db0971aa9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
206 changed files with 1961 additions and 883 deletions

View file

@ -16,10 +16,10 @@ import type { OnApplicationShutdown } from '@nestjs/common';
@Injectable()
export class CacheService implements OnApplicationShutdown {
public userByIdCache: MemoryKVCache<MiUser, MiUser | string>;
public localUserByNativeTokenCache: MemoryKVCache<MiLocalUser | null, string | null>;
public userByIdCache: MemoryKVCache<MiUser>;
public localUserByNativeTokenCache: MemoryKVCache<MiLocalUser | null>;
public localUserByIdCache: MemoryKVCache<MiLocalUser>;
public uriPersonCache: MemoryKVCache<MiUser | null, string | null>;
public uriPersonCache: MemoryKVCache<MiUser | null>;
public userProfileCache: RedisKVCache<MiUserProfile>;
public userMutingsCache: RedisKVCache<Set<string>>;
public userBlockingCache: RedisKVCache<Set<string>>;
@ -56,41 +56,10 @@ export class CacheService implements OnApplicationShutdown {
) {
//this.onMessage = this.onMessage.bind(this);
const localUserByIdCache = new MemoryKVCache<MiLocalUser>(1000 * 60 * 60 * 6 /* 6h */);
this.localUserByIdCache = localUserByIdCache;
// ローカルユーザーならlocalUserByIdCacheにデータを追加し、こちらにはid(文字列)だけを追加する
const userByIdCache = new MemoryKVCache<MiUser, MiUser | string>(1000 * 60 * 60 * 6 /* 6h */, {
toMapConverter: user => {
if (user.host === null) {
localUserByIdCache.set(user.id, user as MiLocalUser);
return user.id;
}
return user;
},
fromMapConverter: userOrId => typeof userOrId === 'string' ? localUserByIdCache.get(userOrId) : userOrId,
});
this.userByIdCache = userByIdCache;
this.localUserByNativeTokenCache = new MemoryKVCache<MiLocalUser | null, string | null>(Infinity, {
toMapConverter: user => {
if (user === null) return null;
localUserByIdCache.set(user.id, user);
return user.id;
},
fromMapConverter: id => id === null ? null : localUserByIdCache.get(id),
});
this.uriPersonCache = new MemoryKVCache<MiUser | null, string | null>(Infinity, {
toMapConverter: user => {
if (user === null) return null;
userByIdCache.set(user.id, user);
return user.id;
},
fromMapConverter: id => id === null ? null : userByIdCache.get(id),
});
this.userByIdCache = new MemoryKVCache<MiUser>(Infinity);
this.localUserByNativeTokenCache = new MemoryKVCache<MiLocalUser | null>(Infinity);
this.localUserByIdCache = new MemoryKVCache<MiLocalUser>(Infinity);
this.uriPersonCache = new MemoryKVCache<MiUser | null>(Infinity);
this.userProfileCache = new RedisKVCache<MiUserProfile>(this.redisClient, 'userProfile', {
lifetime: 1000 * 60 * 30, // 30m
@ -160,16 +129,25 @@ export class CacheService implements OnApplicationShutdown {
switch (type) {
case 'userChangeSuspendedState':
case 'remoteUserUpdated': {
const user = await this.usersRepository.findOneByOrFail({ id: body.id });
this.userByIdCache.set(user.id, user);
for (const [k, v] of this.uriPersonCache.cache.entries()) {
if (v.value === user.id) {
this.uriPersonCache.set(k, user);
const user = await this.usersRepository.findOneBy({ id: body.id });
if (user == null) {
this.userByIdCache.delete(body.id);
for (const [k, v] of this.uriPersonCache.cache.entries()) {
if (v.value?.id === body.id) {
this.uriPersonCache.delete(k);
}
}
} else {
this.userByIdCache.set(user.id, user);
for (const [k, v] of this.uriPersonCache.cache.entries()) {
if (v.value?.id === user.id) {
this.uriPersonCache.set(k, user);
}
}
if (this.userEntityService.isLocalUser(user)) {
this.localUserByNativeTokenCache.set(user.token!, user);
this.localUserByIdCache.set(user.id, user);
}
}
if (this.userEntityService.isLocalUser(user)) {
this.localUserByNativeTokenCache.set(user.token!, user);
this.localUserByIdCache.set(user.id, user);
}
break;
}

View file

@ -30,12 +30,12 @@ import { RoleService } from '@/core/RoleService.js';
import { FeaturedService } from '@/core/FeaturedService.js';
import { trackPromise } from '@/misc/promise-tracker.js';
const FALLBACK = '';
const FALLBACK = '\u2764';
const PER_NOTE_REACTION_USER_PAIR_CACHE_MAX = 16;
const legacies: Record<string, string> = {
'like': '👍',
'love': '❤', // ここに記述する場合は異体字セレクタを入れない
'love': '\u2764', // ハート、異体字セレクタを入れない
'laugh': '😆',
'hmm': '🤔',
'surprise': '😮',
@ -120,7 +120,7 @@ export class ReactionService {
let reaction = _reaction ?? FALLBACK;
if (note.reactionAcceptance === 'likeOnly' || ((note.reactionAcceptance === 'likeOnlyForRemote' || note.reactionAcceptance === 'nonSensitiveOnlyForLocalLikeOnlyForRemote') && (user.host != null))) {
reaction = '❤️';
reaction = '\u2764';
} else if (_reaction) {
const custom = reaction.match(isCustomEmojiRegexp);
if (custom) {

View file

@ -106,12 +106,12 @@ export class ApDbResolverService implements OnApplicationShutdown {
return await this.cacheService.userByIdCache.fetchMaybe(
parsed.id,
() => this.usersRepository.findOneBy({ id: parsed.id }).then(x => x ?? undefined),
() => this.usersRepository.findOneBy({ id: parsed.id, isDeleted: false }).then(x => x ?? undefined),
) as MiLocalUser | undefined ?? null;
} else {
return await this.cacheService.uriPersonCache.fetch(
parsed.uri,
() => this.usersRepository.findOneBy({ uri: parsed.uri }),
() => this.usersRepository.findOneBy({ uri: parsed.uri, isDeleted: false }),
) as MiRemoteUser | null;
}
}
@ -136,8 +136,12 @@ export class ApDbResolverService implements OnApplicationShutdown {
if (key == null) return null;
const user = await this.cacheService.findUserById(key.userId).catch(() => null) as MiRemoteUser | null;
if (user == null) return null;
if (user.isDeleted) return null;
return {
user: await this.cacheService.findUserById(key.userId) as MiRemoteUser,
user,
key,
};
}
@ -151,6 +155,7 @@ export class ApDbResolverService implements OnApplicationShutdown {
key: MiUserPublickey | null;
} | null> {
const user = await this.apPersonService.resolvePerson(uri) as MiRemoteUser;
if (user.isDeleted) return null;
const key = await this.publicKeyByUserIdCache.fetch(
user.id,

View file

@ -35,6 +35,8 @@ import { ApResolverService } from './ApResolverService.js';
import { ApAudienceService } from './ApAudienceService.js';
import { ApPersonService } from './models/ApPersonService.js';
import { ApQuestionService } from './models/ApQuestionService.js';
import { CacheService } from '@/core/CacheService.js';
import { GlobalEventService } from '@/core/GlobalEventService.js';
import type { Resolver } from './ApResolverService.js';
import type { IAccept, IAdd, IAnnounce, IBlock, ICreate, IDelete, IFlag, IFollow, ILike, IObject, IReject, IRemove, IUndo, IUpdate, IMove } from './type.js';
@ -82,6 +84,8 @@ export class ApInboxService {
private apPersonService: ApPersonService,
private apQuestionService: ApQuestionService,
private queueService: QueueService,
private cacheService: CacheService,
private globalEventService: GlobalEventService,
) {
this.logger = this.apLoggerService.logger;
}
@ -479,6 +483,8 @@ export class ApInboxService {
isDeleted: true,
});
this.globalEventService.publishInternalEvent('remoteUserUpdated', { id: actor.id });
return `ok: queued ${job.name} ${job.id}`;
}

View file

@ -186,28 +186,14 @@ export class RedisSingleCache<T> {
// TODO: メモリ節約のためあまり参照されないキーを定期的に削除できるようにする?
function nothingToDo<T, V = T>(value: T): V {
return value as unknown as V;
}
export class MemoryKVCache<T, V = T> {
public cache: Map<string, { date: number; value: V; }>;
export class MemoryKVCache<T> {
public cache: Map<string, { date: number; value: T; }>;
private lifetime: number;
private gcIntervalHandle: NodeJS.Timeout;
private toMapConverter: (value: T) => V;
private fromMapConverter: (cached: V) => T | undefined;
constructor(lifetime: MemoryKVCache<never>['lifetime'], options: {
toMapConverter: (value: T) => V;
fromMapConverter: (cached: V) => T | undefined;
} = {
toMapConverter: nothingToDo,
fromMapConverter: nothingToDo,
}) {
constructor(lifetime: MemoryKVCache<never>['lifetime']) {
this.cache = new Map();
this.lifetime = lifetime;
this.toMapConverter = options.toMapConverter;
this.fromMapConverter = options.fromMapConverter;
this.gcIntervalHandle = setInterval(() => {
this.gc();
@ -218,7 +204,7 @@ export class MemoryKVCache<T, V = T> {
public set(key: string, value: T): void {
this.cache.set(key, {
date: Date.now(),
value: this.toMapConverter(value),
value,
});
}
@ -230,7 +216,7 @@ export class MemoryKVCache<T, V = T> {
this.cache.delete(key);
return undefined;
}
return this.fromMapConverter(cached.value);
return cached.value;
}
@bindThis
@ -241,10 +227,9 @@ export class MemoryKVCache<T, V = T> {
/**
* fetcherを呼び出して結果をキャッシュ&
* optional: キャッシュが存在してもvalidatorでfalseを返すとキャッシュ無効扱いにします
* fetcherの引数はcacheに保存されている値があれば渡されます
*/
@bindThis
public async fetch(key: string, fetcher: (value: V | undefined) => Promise<T>, validator?: (cachedValue: T) => boolean): Promise<T> {
public async fetch(key: string, fetcher: () => Promise<T>, validator?: (cachedValue: T) => boolean): Promise<T> {
const cachedValue = this.get(key);
if (cachedValue !== undefined) {
if (validator) {
@ -259,7 +244,7 @@ export class MemoryKVCache<T, V = T> {
}
// Cache MISS
const value = await fetcher(this.cache.get(key)?.value);
const value = await fetcher();
this.set(key, value);
return value;
}
@ -267,10 +252,9 @@ export class MemoryKVCache<T, V = T> {
/**
* fetcherを呼び出して結果をキャッシュ&
* optional: キャッシュが存在してもvalidatorでfalseを返すとキャッシュ無効扱いにします
* fetcherの引数はcacheに保存されている値があれば渡されます
*/
@bindThis
public async fetchMaybe(key: string, fetcher: (value: V | undefined) => Promise<T | undefined>, validator?: (cachedValue: T) => boolean): Promise<T | undefined> {
public async fetchMaybe(key: string, fetcher: () => Promise<T | undefined>, validator?: (cachedValue: T) => boolean): Promise<T | undefined> {
const cachedValue = this.get(key);
if (cachedValue !== undefined) {
if (validator) {
@ -285,7 +269,7 @@ export class MemoryKVCache<T, V = T> {
}
// Cache MISS
const value = await fetcher(this.cache.get(key)?.value);
const value = await fetcher();
if (value !== undefined) {
this.set(key, value);
}

View file

@ -37,7 +37,17 @@ import { packedEmojiDetailedSchema, packedEmojiSimpleSchema } from '@/models/jso
import { packedFlashSchema } from '@/models/json-schema/flash.js';
import { packedAnnouncementSchema } from '@/models/json-schema/announcement.js';
import { packedSigninSchema } from '@/models/json-schema/signin.js';
import { packedRoleLiteSchema, packedRoleSchema, packedRolePoliciesSchema } from '@/models/json-schema/role.js';
import {
packedRoleLiteSchema,
packedRoleSchema,
packedRolePoliciesSchema,
packedRoleCondFormulaLogicsSchema,
packedRoleCondFormulaValueNot,
packedRoleCondFormulaValueIsLocalOrRemoteSchema,
packedRoleCondFormulaValueCreatedSchema,
packedRoleCondFormulaFollowersOrFollowingOrNotesSchema,
packedRoleCondFormulaValueSchema,
} from '@/models/json-schema/role.js';
import { packedAdSchema } from '@/models/json-schema/ad.js';
import { packedReversiGameLiteSchema, packedReversiGameDetailedSchema } from '@/models/json-schema/reversi-game.js';
@ -78,6 +88,12 @@ export const refs = {
EmojiDetailed: packedEmojiDetailedSchema,
Flash: packedFlashSchema,
Signin: packedSigninSchema,
RoleCondFormulaLogics: packedRoleCondFormulaLogicsSchema,
RoleCondFormulaValueNot: packedRoleCondFormulaValueNot,
RoleCondFormulaValueIsLocalOrRemote: packedRoleCondFormulaValueIsLocalOrRemoteSchema,
RoleCondFormulaValueCreated: packedRoleCondFormulaValueCreatedSchema,
RoleCondFormulaFollowersOrFollowingOrNotes: packedRoleCondFormulaFollowersOrFollowingOrNotesSchema,
RoleCondFormulaValue: packedRoleCondFormulaValueSchema,
RoleLite: packedRoleLiteSchema,
Role: packedRoleSchema,
RolePolicies: packedRolePoliciesSchema,

View file

@ -48,7 +48,7 @@ export class MiBubbleGameRecord {
@Column('jsonb', {
default: [],
})
public logs: any[];
public logs: number[][];
@Column('boolean', {
default: false,

View file

@ -69,7 +69,7 @@ type CondFormulaValueNotesMoreThanOrEq = {
value: number;
};
export type RoleCondFormulaValue =
export type RoleCondFormulaValue = { id: string } & (
CondFormulaValueAnd |
CondFormulaValueOr |
CondFormulaValueNot |
@ -82,7 +82,8 @@ export type RoleCondFormulaValue =
CondFormulaValueFollowingLessThanOrEq |
CondFormulaValueFollowingMoreThanOrEq |
CondFormulaValueNotesLessThanOrEq |
CondFormulaValueNotesMoreThanOrEq;
CondFormulaValueNotesMoreThanOrEq
);
@Entity('role')
export class MiRole {

View file

@ -47,12 +47,12 @@ export const packedReversiGameLiteSchema = {
user1: {
type: 'object',
optional: false, nullable: false,
ref: 'User',
ref: 'UserLite',
},
user2: {
type: 'object',
optional: false, nullable: false,
ref: 'User',
ref: 'UserLite',
},
winnerId: {
type: 'string',
@ -62,7 +62,7 @@ export const packedReversiGameLiteSchema = {
winner: {
type: 'object',
optional: false, nullable: true,
ref: 'User',
ref: 'UserLite',
},
surrenderedUserId: {
type: 'string',
@ -165,12 +165,12 @@ export const packedReversiGameDetailedSchema = {
user1: {
type: 'object',
optional: false, nullable: false,
ref: 'User',
ref: 'UserLite',
},
user2: {
type: 'object',
optional: false, nullable: false,
ref: 'User',
ref: 'UserLite',
},
winnerId: {
type: 'string',
@ -180,7 +180,7 @@ export const packedReversiGameDetailedSchema = {
winner: {
type: 'object',
optional: false, nullable: true,
ref: 'User',
ref: 'UserLite',
},
surrenderedUserId: {
type: 'string',
@ -226,6 +226,9 @@ export const packedReversiGameDetailedSchema = {
items: {
type: 'array',
optional: false, nullable: false,
items: {
type: 'number',
},
},
},
map: {

View file

@ -1,3 +1,129 @@
/*
* SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
export const packedRoleCondFormulaLogicsSchema = {
type: 'object',
properties: {
id: {
type: 'string', optional: false,
},
type: {
type: 'string',
nullable: false, optional: false,
enum: ['and', 'or'],
},
values: {
type: 'array',
nullable: false, optional: false,
items: {
ref: 'RoleCondFormulaValue',
},
},
},
} as const;
export const packedRoleCondFormulaValueNot = {
type: 'object',
properties: {
id: {
type: 'string', optional: false,
},
type: {
type: 'string',
nullable: false, optional: false,
enum: ['not'],
},
value: {
type: 'object',
optional: false,
ref: 'RoleCondFormulaValue',
},
},
} as const;
export const packedRoleCondFormulaValueIsLocalOrRemoteSchema = {
type: 'object',
properties: {
id: {
type: 'string', optional: false,
},
type: {
type: 'string',
nullable: false, optional: false,
enum: ['isLocal', 'isRemote'],
},
},
} as const;
export const packedRoleCondFormulaValueCreatedSchema = {
type: 'object',
properties: {
id: {
type: 'string', optional: false,
},
type: {
type: 'string',
nullable: false, optional: false,
enum: [
'createdLessThan',
'createdMoreThan',
],
},
sec: {
type: 'number',
nullable: false, optional: false,
},
},
} as const;
export const packedRoleCondFormulaFollowersOrFollowingOrNotesSchema = {
type: 'object',
properties: {
id: {
type: 'string', optional: false,
},
type: {
type: 'string',
nullable: false, optional: false,
enum: [
'followersLessThanOrEq',
'followersMoreThanOrEq',
'followingLessThanOrEq',
'followingMoreThanOrEq',
'notesLessThanOrEq',
'notesMoreThanOrEq',
],
},
value: {
type: 'number',
nullable: false, optional: false,
},
},
} as const;
export const packedRoleCondFormulaValueSchema = {
type: 'object',
oneOf: [
{
ref: 'RoleCondFormulaLogics',
},
{
ref: 'RoleCondFormulaValueNot',
},
{
ref: 'RoleCondFormulaValueIsLocalOrRemote',
},
{
ref: 'RoleCondFormulaValueCreated',
},
{
ref: 'RoleCondFormulaFollowersOrFollowingOrNotes',
},
],
} as const;
export const packedRolePoliciesSchema = {
type: 'object',
optional: false, nullable: false,
@ -174,6 +300,7 @@ export const packedRoleSchema = {
condFormula: {
type: 'object',
optional: false, nullable: false,
ref: 'RoleCondFormulaValue',
},
isPublic: {
type: 'boolean',

View file

@ -3,16 +3,38 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
const notificationRecieveConfig = {
export const notificationRecieveConfig = {
type: 'object',
nullable: false, optional: true,
properties: {
type: {
type: 'string',
nullable: false, optional: false,
enum: ['all', 'following', 'follower', 'mutualFollow', 'list', 'never'],
oneOf: [
{
type: 'object',
nullable: false,
properties: {
type: {
type: 'string',
nullable: false,
enum: ['all', 'following', 'follower', 'mutualFollow', 'never'],
},
},
required: ['type'],
},
},
{
type: 'object',
nullable: false,
properties: {
type: {
type: 'string',
nullable: false,
enum: ['list'],
},
userListId: {
type: 'string',
format: 'misskey:id',
},
},
required: ['type', 'userListId'],
},
],
} as const;
export const packedUserLiteSchema = {
@ -546,15 +568,20 @@ export const packedMeDetailedOnlySchema = {
type: 'object',
nullable: false, optional: false,
properties: {
app: notificationRecieveConfig,
quote: notificationRecieveConfig,
reply: notificationRecieveConfig,
follow: notificationRecieveConfig,
renote: notificationRecieveConfig,
mention: notificationRecieveConfig,
reaction: notificationRecieveConfig,
pollEnded: notificationRecieveConfig,
receiveFollowRequest: notificationRecieveConfig,
note: { optional: true, ...notificationRecieveConfig },
follow: { optional: true, ...notificationRecieveConfig },
mention: { optional: true, ...notificationRecieveConfig },
reply: { optional: true, ...notificationRecieveConfig },
renote: { optional: true, ...notificationRecieveConfig },
quote: { optional: true, ...notificationRecieveConfig },
reaction: { optional: true, ...notificationRecieveConfig },
pollEnded: { optional: true, ...notificationRecieveConfig },
receiveFollowRequest: { optional: true, ...notificationRecieveConfig },
followRequestAccepted: { optional: true, ...notificationRecieveConfig },
roleAssigned: { optional: true, ...notificationRecieveConfig },
achievementEarned: { optional: true, ...notificationRecieveConfig },
app: { optional: true, ...notificationRecieveConfig },
test: { optional: true, ...notificationRecieveConfig },
},
},
emailNotificationTypes: {

View file

@ -15,9 +15,6 @@ export const meta = {
requireCredential: true,
requireAdmin: true,
kind: 'write:admin:delete-account',
res: {
},
} as const;
export const paramDef = {

View file

@ -84,6 +84,24 @@ export const meta = {
properties: {
type: 'object',
optional: false, nullable: false,
properties: {
width: {
type: 'number',
optional: true, nullable: false,
},
height: {
type: 'number',
optional: true, nullable: false,
},
orientation: {
type: 'number',
optional: true, nullable: false,
},
avgColor: {
type: 'string',
optional: true, nullable: false,
},
},
},
storedInternal: {
type: 'boolean',

View file

@ -18,6 +18,18 @@ export const meta = {
res: {
type: 'object',
optional: false, nullable: false,
additionalProperties: {
type: 'object',
properties: {
count: {
type: 'number',
},
size: {
type: 'number',
},
},
required: ['count', 'size'],
},
example: {
migrations: {
count: 66,

View file

@ -10,6 +10,7 @@ import { DI } from '@/di-symbols.js';
import { RoleService } from '@/core/RoleService.js';
import { RoleEntityService } from '@/core/entities/RoleEntityService.js';
import { IdService } from '@/core/IdService.js';
import { notificationRecieveConfig } from '@/models/json-schema/user.js';
export const meta = {
tags: ['admin'],
@ -21,6 +22,157 @@ export const meta = {
res: {
type: 'object',
nullable: false, optional: false,
properties: {
email: {
type: 'string',
optional: false, nullable: true,
},
emailVerified: {
type: 'boolean',
optional: false, nullable: false,
},
autoAcceptFollowed: {
type: 'boolean',
optional: false, nullable: false,
},
noCrawle: {
type: 'boolean',
optional: false, nullable: false,
},
preventAiLearning: {
type: 'boolean',
optional: false, nullable: false,
},
alwaysMarkNsfw: {
type: 'boolean',
optional: false, nullable: false,
},
autoSensitive: {
type: 'boolean',
optional: false, nullable: false,
},
carefulBot: {
type: 'boolean',
optional: false, nullable: false,
},
injectFeaturedNote: {
type: 'boolean',
optional: false, nullable: false,
},
receiveAnnouncementEmail: {
type: 'boolean',
optional: false, nullable: false,
},
mutedWords: {
type: 'array',
optional: false, nullable: false,
items: {
anyOf: [
{
type: 'string',
},
{
type: 'array',
items: {
type: 'string',
},
},
],
},
},
mutedInstances: {
type: 'array',
optional: false, nullable: false,
items: {
type: 'string',
},
},
notificationRecieveConfig: {
type: 'object',
optional: false, nullable: false,
properties: {
note: { optional: true, ...notificationRecieveConfig },
follow: { optional: true, ...notificationRecieveConfig },
mention: { optional: true, ...notificationRecieveConfig },
reply: { optional: true, ...notificationRecieveConfig },
renote: { optional: true, ...notificationRecieveConfig },
quote: { optional: true, ...notificationRecieveConfig },
reaction: { optional: true, ...notificationRecieveConfig },
pollEnded: { optional: true, ...notificationRecieveConfig },
receiveFollowRequest: { optional: true, ...notificationRecieveConfig },
followRequestAccepted: { optional: true, ...notificationRecieveConfig },
roleAssigned: { optional: true, ...notificationRecieveConfig },
achievementEarned: { optional: true, ...notificationRecieveConfig },
app: { optional: true, ...notificationRecieveConfig },
test: { optional: true, ...notificationRecieveConfig },
},
},
isModerator: {
type: 'boolean',
optional: false, nullable: false,
},
isSilenced: {
type: 'boolean',
optional: false, nullable: false,
},
isSuspended: {
type: 'boolean',
optional: false, nullable: false,
},
isHibernated: {
type: 'boolean',
optional: false, nullable: false,
},
lastActiveDate: {
type: 'string',
optional: false, nullable: true,
},
moderationNote: {
type: 'string',
optional: false, nullable: false,
},
signins: {
type: 'array',
optional: false, nullable: false,
items: {
ref: 'Signin',
},
},
policies: {
type: 'object',
optional: false, nullable: false,
ref: 'RolePolicies',
},
roles: {
type: 'array',
optional: false, nullable: false,
items: {
type: 'object',
ref: 'Role',
},
},
roleAssigns: {
type: 'array',
optional: false, nullable: false,
items: {
type: 'object',
properties: {
createdAt: {
type: 'string',
optional: false, nullable: false,
},
expiresAt: {
type: 'string',
optional: false, nullable: true,
},
roleId: {
type: 'string',
optional: false, nullable: false,
},
},
},
},
},
},
} as const;
@ -89,7 +241,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
isSilenced: isSilenced,
isSuspended: user.isSuspended,
isHibernated: user.isHibernated,
lastActiveDate: user.lastActiveDate,
lastActiveDate: user.lastActiveDate ? user.lastActiveDate.toISOString() : null,
moderationNote: profile.moderationNote ?? '',
signins,
policies: await this.roleService.getUserPolicies(user.id),

View file

@ -24,9 +24,19 @@ export const meta = {
type: 'object',
optional: false, nullable: false,
properties: {
id: { type: 'string', format: 'misskey:id' },
score: { type: 'integer' },
user: { ref: 'UserLite' },
id: {
type: 'string', format: 'misskey:id',
optional: false, nullable: false,
},
score: {
type: 'integer',
optional: false, nullable: false,
},
user: {
type: 'object',
optional: true, nullable: false,
ref: 'UserLite',
},
},
},
},

View file

@ -29,9 +29,6 @@ export const meta = {
id: 'eb627bc7-574b-4a52-a860-3c3eae772b88',
},
},
res: {
},
} as const;
export const paramDef = {
@ -39,7 +36,15 @@ export const paramDef = {
properties: {
score: { type: 'integer', minimum: 0 },
seed: { type: 'string', minLength: 1, maxLength: 1024 },
logs: { type: 'array' },
logs: {
type: 'array',
items: {
type: 'array',
items: {
type: 'number',
},
},
},
gameMode: { type: 'string' },
gameVersion: { type: 'integer' },
},

View file

@ -15,6 +15,19 @@ export const meta = {
requireCredential: true,
secure: true,
res: {
type: 'object',
properties: {
backupCodes: {
type: 'array',
optional: false,
items: {
type: 'string',
},
},
},
},
} as const;
export const paramDef = {

View file

@ -47,7 +47,7 @@ export const meta = {
properties: {
id: {
type: 'string',
nullable: true,
optional: true,
},
},
},

View file

@ -21,26 +21,31 @@ export const meta = {
properties: {
id: {
type: 'string',
optional: false,
format: 'misskey:id',
},
name: {
type: 'string',
optional: true,
},
createdAt: {
type: 'string',
optional: false,
format: 'date-time',
},
lastUsedAt: {
type: 'string',
optional: true,
format: 'date-time',
},
permission: {
type: 'array',
optional: false,
uniqueItems: true,
items: {
type: 'string'
type: 'string',
},
}
},
},
},
},

View file

@ -23,23 +23,27 @@ export const meta = {
id: {
type: 'string',
format: 'misskey:id',
optional: false,
},
name: {
type: 'string',
optional: false,
},
callbackUrl: {
type: 'string',
nullable: true,
optional: false, nullable: true,
},
permission: {
type: 'array',
optional: false,
uniqueItems: true,
items: {
type: 'string'
type: 'string',
},
},
isAuthorized: {
type: 'boolean',
optional: true,
},
},
},

View file

@ -22,7 +22,16 @@ export const meta = {
res: {
type: 'object',
}
properties: {
updatedAt: {
type: 'string',
optional: false,
},
value: {
optional: false,
},
},
},
} as const;
export const paramDef = {
@ -50,7 +59,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
}
return {
updatedAt: item.updatedAt,
updatedAt: item.updatedAt.toISOString(),
value: item.value,
};
});

View file

@ -13,6 +13,9 @@ export const meta = {
res: {
type: 'object',
additionalProperties: {
type: 'string',
},
},
} as const;

View file

@ -10,6 +10,13 @@ import { RegistryApiService } from '@/core/RegistryApiService.js';
export const meta = {
requireCredential: true,
kind: 'read:account',
res: {
type: 'array',
items: {
type: 'string',
},
},
} as const;
export const paramDef = {

View file

@ -33,6 +33,7 @@ import { HttpRequestService } from '@/core/HttpRequestService.js';
import type { Config } from '@/config.js';
import { safeForSql } from '@/misc/safe-for-sql.js';
import { AvatarDecorationService } from '@/core/AvatarDecorationService.js';
import { notificationRecieveConfig } from '@/models/json-schema/user.js';
import { ApiLoggerService } from '../../ApiLoggerService.js';
import { ApiError } from '../../error.js';
@ -184,7 +185,26 @@ export const paramDef = {
mutedInstances: { type: 'array', items: {
type: 'string',
} },
notificationRecieveConfig: { type: 'object' },
notificationRecieveConfig: {
type: 'object',
nullable: false,
properties: {
note: notificationRecieveConfig,
follow: notificationRecieveConfig,
mention: notificationRecieveConfig,
reply: notificationRecieveConfig,
renote: notificationRecieveConfig,
quote: notificationRecieveConfig,
reaction: notificationRecieveConfig,
pollEnded: notificationRecieveConfig,
receiveFollowRequest: notificationRecieveConfig,
followRequestAccepted: notificationRecieveConfig,
roleAssigned: notificationRecieveConfig,
achievementEarned: notificationRecieveConfig,
app: notificationRecieveConfig,
test: notificationRecieveConfig,
},
},
emailNotificationTypes: { type: 'array', items: {
type: 'string',
} },

View file

@ -33,7 +33,7 @@ export const meta = {
properties: {
id: {
type: 'string',
format: 'misskey:id'
format: 'misskey:id',
},
userId: {
type: 'string',
@ -45,7 +45,7 @@ export const meta = {
items: {
type: 'string',
enum: webhookEventTypes,
}
},
},
url: { type: 'string' },
secret: { type: 'string' },
@ -108,7 +108,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
url: webhook.url,
secret: webhook.secret,
active: webhook.active,
latestSentAt: webhook.latestSentAt?.toISOString(),
latestSentAt: webhook.latestSentAt ? webhook.latestSentAt.toISOString() : null,
latestStatus: webhook.latestStatus,
};
});

View file

@ -23,7 +23,7 @@ export const meta = {
properties: {
id: {
type: 'string',
format: 'misskey:id'
format: 'misskey:id',
},
userId: {
type: 'string',
@ -35,7 +35,7 @@ export const meta = {
items: {
type: 'string',
enum: webhookEventTypes,
}
},
},
url: { type: 'string' },
secret: { type: 'string' },
@ -43,8 +43,8 @@ export const meta = {
latestSentAt: { type: 'string', format: 'date-time', nullable: true },
latestStatus: { type: 'integer', nullable: true },
},
}
}
},
},
} as const;
export const paramDef = {
@ -73,7 +73,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
url: webhook.url,
secret: webhook.secret,
active: webhook.active,
latestSentAt: webhook.latestSentAt?.toISOString(),
latestSentAt: webhook.latestSentAt ? webhook.latestSentAt.toISOString() : null,
latestStatus: webhook.latestStatus,
}
));

View file

@ -30,7 +30,7 @@ export const meta = {
properties: {
id: {
type: 'string',
format: 'misskey:id'
format: 'misskey:id',
},
userId: {
type: 'string',
@ -42,7 +42,7 @@ export const meta = {
items: {
type: 'string',
enum: webhookEventTypes,
}
},
},
url: { type: 'string' },
secret: { type: 'string' },
@ -85,7 +85,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
url: webhook.url,
secret: webhook.secret,
active: webhook.active,
latestSentAt: webhook.latestSentAt?.toISOString(),
latestSentAt: webhook.latestSentAt ? webhook.latestSentAt.toISOString() : null,
latestStatus: webhook.latestStatus,
};
});

View file

@ -14,9 +14,6 @@ export const meta = {
errors: {
},
res: {
},
} as const;
export const paramDef = {

View file

@ -30,6 +30,9 @@ export const meta = {
},
res: {
type: 'object',
optional: true,
ref: 'ReversiGameDetailed',
},
} as const;

View file

@ -18,24 +18,28 @@ export const meta = {
properties: {
id: {
type: 'string',
format: 'misskey:id'
format: 'misskey:id',
optional: true, nullable: false,
},
required: {
type: 'boolean',
optional: false, nullable: false,
},
string: {
type: 'string',
optional: true, nullable: false,
},
default: {
type: 'string',
optional: true, nullable: false,
},
nullableDefault: {
type: 'string',
default: 'hello',
nullable: true,
optional: true, nullable: true,
},
}
}
},
},
} as const;
export const paramDef = {

View file

@ -28,7 +28,7 @@
"@tabler/icons-webfont": "2.44.0",
"@twemoji/parser": "15.0.0",
"@vitejs/plugin-vue": "5.0.3",
"@vue/compiler-sfc": "3.4.15",
"@vue/compiler-sfc": "3.4.18",
"aiscript-vscode": "github:aiscript-dev/aiscript-vscode#v0.1.2",
"astring": "1.8.6",
"broadcast-channel": "7.0.0",
@ -72,7 +72,7 @@
"uuid": "9.0.1",
"v-code-diff": "1.7.2",
"vite": "5.1.0",
"vue": "3.4.15",
"vue": "3.4.18",
"vuedraggable": "next"
},
"devDependencies": {

View file

@ -16,9 +16,9 @@ SPDX-License-Identifier: AGPL-3.0-only
@closed="$emit('closed')"
>
<template #header>
<template v-if="pageMetadata?.value">
<i v-if="pageMetadata.value.icon" :class="pageMetadata.value.icon" style="margin-right: 0.5em;"></i>
<span>{{ pageMetadata.value.title }}</span>
<template v-if="pageMetadata">
<i v-if="pageMetadata.icon" :class="pageMetadata.icon" style="margin-right: 0.5em;"></i>
<span>{{ pageMetadata.title }}</span>
</template>
</template>
@ -29,7 +29,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<script lang="ts" setup>
import { computed, ComputedRef, onMounted, onUnmounted, provide, ref, shallowRef } from 'vue';
import { computed, onMounted, onUnmounted, provide, ref, shallowRef } from 'vue';
import RouterView from '@/components/global/RouterView.vue';
import MkWindow from '@/components/MkWindow.vue';
import { popout as _popout } from '@/scripts/popout.js';
@ -37,7 +37,7 @@ import copyToClipboard from '@/scripts/copy-to-clipboard.js';
import { url } from '@/config.js';
import { useScrollPositionManager } from '@/nirax.js';
import { i18n } from '@/i18n.js';
import { PageMetadata, provideMetadataReceiver } from '@/scripts/page-metadata.js';
import { PageMetadata, provideMetadataReceiver, provideReactiveMetadata } from '@/scripts/page-metadata.js';
import { openingWindowsCount } from '@/os.js';
import { claimAchievement } from '@/scripts/achievements.js';
import { getScrollContainer } from '@/scripts/scroll.js';
@ -56,7 +56,7 @@ const routerFactory = useRouterFactory();
const windowRouter = routerFactory(props.initialPath);
const contents = shallowRef<HTMLElement | null>(null);
const pageMetadata = ref<null | ComputedRef<PageMetadata>>();
const pageMetadata = ref<null | PageMetadata>(null);
const windowEl = shallowRef<InstanceType<typeof MkWindow>>();
const history = ref<{ path: string; key: any; }[]>([{
path: windowRouter.getCurrentPath(),
@ -101,9 +101,11 @@ windowRouter.addListener('replace', ctx => {
windowRouter.init();
provide('router', windowRouter);
provideMetadataReceiver((info) => {
provideMetadataReceiver((metadataGetter) => {
const info = metadataGetter();
pageMetadata.value = info;
});
provideReactiveMetadata(pageMetadata);
provide('shouldOmitHeaderTitle', true);
provide('shouldHeaderThin', true);
provide('forceSpacerMin', true);

View file

@ -11,18 +11,18 @@ SPDX-License-Identifier: AGPL-3.0-only
</div>
<div v-else-if="!thin_ && narrow && !hideTitle" :class="$style.buttonsLeft"/>
<template v-if="metadata">
<template v-if="pageMetadata">
<div v-if="!hideTitle" :class="$style.titleContainer" @click="top">
<div v-if="metadata.avatar" :class="$style.titleAvatarContainer">
<MkAvatar :class="$style.titleAvatar" :user="metadata.avatar" indicator/>
<div v-if="pageMetadata.avatar" :class="$style.titleAvatarContainer">
<MkAvatar :class="$style.titleAvatar" :user="pageMetadata.avatar" indicator/>
</div>
<i v-else-if="metadata.icon" :class="[$style.titleIcon, metadata.icon]"></i>
<i v-else-if="pageMetadata.icon" :class="[$style.titleIcon, pageMetadata.icon]"></i>
<div :class="$style.title">
<MkUserName v-if="metadata.userName" :user="metadata.userName" :nowrap="true"/>
<div v-else-if="metadata.title">{{ metadata.title }}</div>
<div v-if="metadata.subtitle" :class="$style.subtitle">
{{ metadata.subtitle }}
<MkUserName v-if="pageMetadata.userName" :user="pageMetadata.userName" :nowrap="true"/>
<div v-else-if="pageMetadata.title">{{ pageMetadata.title }}</div>
<div v-if="pageMetadata.subtitle" :class="$style.subtitle">
{{ pageMetadata.subtitle }}
</div>
</div>
</div>
@ -46,7 +46,7 @@ import tinycolor from 'tinycolor2';
import XTabs, { Tab } from './MkPageHeader.tabs.vue';
import { scrollToTop } from '@/scripts/scroll.js';
import { globalEvents } from '@/events.js';
import { injectPageMetadata } from '@/scripts/page-metadata.js';
import { injectReactiveMetadata } from '@/scripts/page-metadata.js';
import { $i, openAccountMenu as openAccountMenu_ } from '@/account.js';
import { PageHeaderItem } from '@/types/page-header.js';
@ -64,7 +64,7 @@ const emit = defineEmits<{
(ev: 'update:tab', key: string);
}>();
const metadata = injectPageMetadata();
const pageMetadata = injectReactiveMetadata();
const hideTitle = inject('shouldOmitHeaderTitle', false);
const thin_ = props.thin || inject('shouldHeaderThin', false);

View file

@ -18,7 +18,7 @@ export const langs = _LANGS_;
const preParseLocale = miLocalStorage.getItem('locale');
export let locale = preParseLocale ? JSON.parse(preParseLocale) : null;
export const version = _VERSION_;
export const instanceName = siteName === 'Misskey' ? host : siteName;
export const instanceName = siteName === 'Misskey' || siteName == null ? host : siteName;
export const ui = miLocalStorage.getItem('ui');
export const debug = miLocalStorage.getItem('debug') === 'true';

View file

@ -67,10 +67,10 @@ const headerActions = computed(() => []);
const headerTabs = computed(() => []);
definePageMetadata({
definePageMetadata(() => ({
title: i18n.ts.error,
icon: 'ti ti-alert-triangle',
});
}));
</script>
<style lang="scss" module>

View file

@ -363,10 +363,10 @@ const headerActions = computed(() => []);
const headerTabs = computed(() => []);
definePageMetadata({
definePageMetadata(() => ({
title: i18n.ts.aboutMisskey,
icon: null,
});
}));
</script>
<style lang="scss" scoped>

View file

@ -164,10 +164,10 @@ const headerTabs = computed(() => [{
icon: 'ti ti-chart-line',
}]);
definePageMetadata(computed(() => ({
definePageMetadata(() => ({
title: i18n.ts.instanceInfo,
icon: 'ti ti-info-circle',
})));
}));
</script>
<style lang="scss" module>

View file

@ -48,10 +48,10 @@ onDeactivated(() => {
}
});
definePageMetadata({
definePageMetadata(() => ({
title: i18n.ts.achievements,
icon: 'ti ti-medal',
});
}));
</script>
<style lang="scss" module>

View file

@ -140,10 +140,10 @@ const headerTabs = computed(() => [{
icon: 'ti ti-code',
}]);
definePageMetadata(computed(() => ({
title: file.value ? i18n.ts.file + ': ' + file.value.name : i18n.ts.file,
definePageMetadata(() => ({
title: file.value ? `${i18n.ts.file}: ${file.value.name}` : i18n.ts.file,
icon: 'ti ti-file',
})));
}));
</script>
<style lang="scss" scoped>

View file

@ -518,10 +518,10 @@ const headerTabs = computed(() => [{
icon: 'ti ti-code',
}]);
definePageMetadata(computed(() => ({
definePageMetadata(() => ({
title: user.value ? acct(user.value) : i18n.ts.userInfo,
icon: 'ti ti-user-exclamation',
})));
}));
</script>
<style lang="scss" scoped>

View file

@ -5,12 +5,12 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<div ref="el" class="fdidabkc" :style="{ background: bg }" @click="onClick">
<template v-if="metadata">
<template v-if="pageMetadata">
<div class="titleContainer" @click="showTabsPopup">
<i v-if="metadata.icon" class="icon" :class="metadata.icon"></i>
<i v-if="pageMetadata.icon" class="icon" :class="pageMetadata.icon"></i>
<div class="title">
<div class="title">{{ metadata.title }}</div>
<div class="title">{{ pageMetadata.title }}</div>
</div>
</div>
<div class="tabs">
@ -39,7 +39,7 @@ import { popupMenu } from '@/os.js';
import { scrollToTop } from '@/scripts/scroll.js';
import MkButton from '@/components/MkButton.vue';
import { globalEvents } from '@/events.js';
import { injectPageMetadata } from '@/scripts/page-metadata.js';
import { injectReactiveMetadata } from '@/scripts/page-metadata.js';
type Tab = {
key?: string | null;
@ -65,7 +65,7 @@ const emit = defineEmits<{
(ev: 'update:tab', key: string);
}>();
const metadata = injectPageMetadata();
const pageMetadata = injectReactiveMetadata();
const el = shallowRef<HTMLElement>(null);
const tabRefs = {};
@ -118,7 +118,7 @@ function onTabClick(tab: Tab, ev: MouseEvent): void {
}
const calcBg = () => {
const rawBg = metadata?.bg ?? 'var(--bg)';
const rawBg = pageMetadata.value?.bg ?? 'var(--bg)';
const tinyBg = tinycolor(rawBg.startsWith('var(') ? getComputedStyle(document.documentElement).getPropertyValue(rawBg.slice(4, -1)) : rawBg);
tinyBg.setAlpha(0.85);
bg.value = tinyBg.toRgbString();

View file

@ -87,8 +87,8 @@ const headerActions = computed(() => []);
const headerTabs = computed(() => []);
definePageMetadata({
definePageMetadata(() => ({
title: i18n.ts.abuseReports,
icon: 'ti ti-exclamation-circle',
});
}));
</script>

View file

@ -255,10 +255,10 @@ const headerActions = computed(() => [{
const headerTabs = computed(() => []);
definePageMetadata({
definePageMetadata(() => ({
title: i18n.ts.ads,
icon: 'ti ti-ad',
});
}));
</script>
<style lang="scss" module>

View file

@ -157,8 +157,8 @@ const headerActions = computed(() => [{
const headerTabs = computed(() => []);
definePageMetadata({
definePageMetadata(() => ({
title: i18n.ts.announcements,
icon: 'ti ti-speakerphone',
});
}));
</script>

View file

@ -159,10 +159,10 @@ function save() {
const headerTabs = computed(() => []);
definePageMetadata({
definePageMetadata(() => ({
title: i18n.ts.branding,
icon: 'ti ti-paint',
});
}));
</script>
<style lang="scss" module>

View file

@ -33,8 +33,8 @@ const headerActions = computed(() => []);
const headerTabs = computed(() => []);
definePageMetadata({
definePageMetadata(() => ({
title: i18n.ts.database,
icon: 'ti ti-database',
});
}));
</script>

View file

@ -130,10 +130,10 @@ function save() {
const headerTabs = computed(() => []);
definePageMetadata({
definePageMetadata(() => ({
title: i18n.ts.emailServer,
icon: 'ti ti-mail',
});
}));
</script>
<style lang="scss" module>

View file

@ -69,10 +69,10 @@ const headerActions = computed(() => []);
const headerTabs = computed(() => []);
definePageMetadata({
definePageMetadata(() => ({
title: i18n.ts.externalServices,
icon: 'ti ti-link',
});
}));
</script>
<style lang="scss" module>

View file

@ -102,10 +102,10 @@ const headerActions = computed(() => []);
const headerTabs = computed(() => []);
definePageMetadata(computed(() => ({
definePageMetadata(() => ({
title: i18n.ts.federation,
icon: 'ti ti-whirl',
})));
}));
</script>
<style lang="scss" module>

View file

@ -108,8 +108,8 @@ const headerActions = computed(() => [{
const headerTabs = computed(() => []);
definePageMetadata(computed(() => ({
definePageMetadata(() => ({
title: i18n.ts.files,
icon: 'ti ti-cloud',
})));
}));
</script>

View file

@ -28,7 +28,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<script lang="ts" setup>
import { ComputedRef, Ref, onActivated, onMounted, onUnmounted, provide, watch, ref, computed } from 'vue';
import { onActivated, onMounted, onUnmounted, provide, watch, ref, computed } from 'vue';
import { i18n } from '@/i18n.js';
import MkSuperMenu from '@/components/MkSuperMenu.vue';
import MkInfo from '@/components/MkInfo.vue';
@ -36,7 +36,7 @@ import { instance } from '@/instance.js';
import * as os from '@/os.js';
import { misskeyApi } from '@/scripts/misskey-api.js';
import { lookupUser, lookupUserByEmail } from '@/scripts/lookup-user.js';
import { PageMetadata, definePageMetadata, provideMetadataReceiver } from '@/scripts/page-metadata.js';
import { PageMetadata, definePageMetadata, provideMetadataReceiver, provideReactiveMetadata } from '@/scripts/page-metadata.js';
import { useRouter } from '@/router/supplier.js';
const isEmpty = (x: string | null) => x == null || x === '';
@ -52,7 +52,7 @@ const indexInfo = {
provide('shouldOmitHeaderTitle', false);
const INFO = ref(indexInfo);
const childInfo: Ref<ComputedRef<PageMetadata> | null> = ref(null);
const childInfo = ref<null | PageMetadata>(null);
const narrow = ref(false);
const view = ref(null);
const el = ref<HTMLDivElement | null>(null);
@ -257,14 +257,16 @@ watch(router.currentRef, (to) => {
}
});
provideMetadataReceiver((info) => {
provideMetadataReceiver((metadataGetter) => {
const info = metadataGetter();
if (info == null) {
childInfo.value = null;
} else {
childInfo.value = info;
INFO.value.needWideArea = info.value.needWideArea ?? undefined;
INFO.value.needWideArea = info.needWideArea ?? undefined;
}
});
provideReactiveMetadata(INFO);
function invite() {
misskeyApi('admin/invite/create').then(x => {
@ -318,7 +320,7 @@ const headerActions = computed(() => []);
const headerTabs = computed(() => []);
definePageMetadata(INFO.value);
definePageMetadata(() => INFO.value);
defineExpose({
header: {

View file

@ -66,8 +66,8 @@ const headerTabs = computed(() => [{
icon: 'ti ti-eye-off',
}]);
definePageMetadata({
definePageMetadata(() => ({
title: i18n.ts.instanceBlocking,
icon: 'ti ti-ban',
});
}));
</script>

View file

@ -113,10 +113,10 @@ function deleted(id: string) {
const headerActions = computed(() => []);
const headerTabs = computed(() => []);
definePageMetadata({
definePageMetadata(() => ({
title: i18n.ts.invite,
icon: 'ti ti-user-plus',
});
}));
</script>
<style lang="scss" module>

View file

@ -116,10 +116,10 @@ function save() {
const headerTabs = computed(() => []);
definePageMetadata({
definePageMetadata(() => ({
title: i18n.ts.moderation,
icon: 'ti ti-shield',
});
}));
</script>
<style lang="scss" module>

View file

@ -60,8 +60,8 @@ const headerActions = computed(() => []);
const headerTabs = computed(() => []);
definePageMetadata({
definePageMetadata(() => ({
title: i18n.ts.moderationLogs,
icon: 'ti ti-list-search',
});
}));
</script>

View file

@ -149,10 +149,10 @@ function save() {
const headerTabs = computed(() => []);
definePageMetadata({
definePageMetadata(() => ({
title: i18n.ts.objectStorage,
icon: 'ti ti-cloud',
});
}));
</script>
<style lang="scss" module>

View file

@ -86,8 +86,8 @@ const headerActions = computed(() => [{
const headerTabs = computed(() => []);
definePageMetadata({
definePageMetadata(() => ({
title: i18n.ts.other,
icon: 'ti ti-adjustments',
});
}));
</script>

View file

@ -184,10 +184,10 @@ const headerActions = computed(() => []);
const headerTabs = computed(() => []);
definePageMetadata({
definePageMetadata(() => ({
title: i18n.ts.dashboard,
icon: 'ti ti-dashboard',
});
}));
</script>
<style lang="scss" module>

View file

@ -64,8 +64,8 @@ const headerActions = computed(() => []);
const headerTabs = computed(() => []);
definePageMetadata({
definePageMetadata(() => ({
title: i18n.ts.proxyAccount,
icon: 'ti ti-ghost',
});
}));
</script>

View file

@ -68,8 +68,8 @@ const headerTabs = computed(() => [{
title: 'Inbox',
}]);
definePageMetadata({
definePageMetadata(() => ({
title: i18n.ts.jobQueue,
icon: 'ti ti-clock-play',
});
}));
</script>

View file

@ -84,10 +84,10 @@ const headerActions = computed(() => [{
const headerTabs = computed(() => []);
definePageMetadata({
definePageMetadata(() => ({
title: i18n.ts.relays,
icon: 'ti ti-planet',
});
}));
</script>
<style lang="scss" module>

View file

@ -87,11 +87,8 @@ async function save() {
const headerTabs = computed(() => []);
definePageMetadata(computed(() => role.value ? {
title: i18n.ts._role.edit + ': ' + role.value.name,
icon: 'ti ti-badge',
} : {
title: i18n.ts._role.new,
definePageMetadata(() => ({
title: role.value ? `${i18n.ts._role.edit}: ${role.value.name}` : i18n.ts._role.new,
icon: 'ti ti-badge',
}));
</script>

View file

@ -170,10 +170,10 @@ const headerActions = computed(() => []);
const headerTabs = computed(() => []);
definePageMetadata(computed(() => ({
title: i18n.ts.role + ': ' + role.name,
definePageMetadata(() => ({
title: `${i18n.ts.role}: ${role.name}`,
icon: 'ti ti-badge',
})));
}));
</script>
<style lang="scss" module>

View file

@ -270,10 +270,10 @@ const headerActions = computed(() => []);
const headerTabs = computed(() => []);
definePageMetadata(computed(() => ({
definePageMetadata(() => ({
title: i18n.ts.roles,
icon: 'ti ti-badges',
})));
}));
</script>
<style lang="scss" module>

View file

@ -229,8 +229,8 @@ const headerActions = computed(() => []);
const headerTabs = computed(() => []);
definePageMetadata({
definePageMetadata(() => ({
title: i18n.ts.security,
icon: 'ti ti-lock',
});
}));
</script>

View file

@ -67,10 +67,10 @@ const remove = (index: number): void => {
const headerTabs = computed(() => []);
definePageMetadata({
definePageMetadata(() => ({
title: i18n.ts.serverRules,
icon: 'ti ti-checkbox',
});
}));
</script>
<style lang="scss" module>

View file

@ -235,10 +235,10 @@ async function save(): void {
const headerTabs = computed(() => []);
definePageMetadata({
definePageMetadata(() => ({
title: i18n.ts.general,
icon: 'ti ti-settings',
});
}));
</script>
<style lang="scss" module>

View file

@ -137,10 +137,10 @@ const headerActions = computed(() => [{
const headerTabs = computed(() => []);
definePageMetadata(computed(() => ({
definePageMetadata(() => ({
title: i18n.ts.users,
icon: 'ti ti-users',
})));
}));
</script>
<style lang="scss" module>

View file

@ -20,9 +20,9 @@ import { definePageMetadata } from '@/scripts/page-metadata.js';
import { i18n } from '@/i18n.js';
import { instance } from '@/instance.js';
definePageMetadata({
definePageMetadata(() => ({
title: i18n.ts.ads,
icon: 'ti ti-ad',
});
}));
</script>

View file

@ -106,10 +106,10 @@ const headerTabs = computed(() => [{
icon: 'ti ti-point',
}]);
definePageMetadata({
definePageMetadata(() => ({
title: i18n.ts.announcements,
icon: 'ti ti-speakerphone',
});
}));
</script>
<style lang="scss" module>

View file

@ -91,10 +91,10 @@ const headerActions = computed(() => antenna.value ? [{
const headerTabs = computed(() => []);
definePageMetadata(computed(() => antenna.value ? {
title: antenna.value.name,
definePageMetadata(() => ({
title: antenna.value ? antenna.value.name : i18n.ts.antennas,
icon: 'ti ti-antenna',
} : null));
}));
</script>
<style lang="scss" module>

View file

@ -87,8 +87,8 @@ const headerActions = computed(() => []);
const headerTabs = computed(() => []);
definePageMetadata({
definePageMetadata(() => ({
title: 'API console',
icon: 'ti ti-terminal-2',
});
}));
</script>

View file

@ -97,10 +97,10 @@ const headerActions = computed(() => []);
const headerTabs = computed(() => []);
definePageMetadata({
definePageMetadata(() => ({
title: i18n.ts._auth.shareAccessTitle,
icon: 'ti ti-apps',
});
}));
</script>
<style lang="scss" module>

View file

@ -94,8 +94,8 @@ const headerActions = computed(() => [{
const headerTabs = computed(() => []);
definePageMetadata({
definePageMetadata(() => ({
title: i18n.ts.avatarDecorations,
icon: 'ti ti-sparkles',
});
}));
</script>

View file

@ -202,11 +202,8 @@ const headerActions = computed(() => []);
const headerTabs = computed(() => []);
definePageMetadata(computed(() => props.channelId ? {
title: i18n.ts._channel.edit,
icon: 'ti ti-device-tv',
} : {
title: i18n.ts._channel.create,
definePageMetadata(() => ({
title: props.channelId ? i18n.ts._channel.edit : i18n.ts._channel.create,
icon: 'ti ti-device-tv',
}));
</script>

View file

@ -258,10 +258,10 @@ const headerTabs = computed(() => [{
icon: 'ti ti-search',
}]);
definePageMetadata(computed(() => channel.value ? {
title: channel.value.name,
definePageMetadata(() => ({
title: channel.value ? channel.value.name : i18n.ts.channel,
icon: 'ti ti-device-tv',
} : null));
}));
</script>
<style lang="scss" module>

View file

@ -152,8 +152,8 @@ const headerTabs = computed(() => [{
icon: 'ti ti-edit',
}]);
definePageMetadata(computed(() => ({
definePageMetadata(() => ({
title: i18n.ts.channel,
icon: 'ti ti-device-tv',
})));
}));
</script>

View file

@ -16,10 +16,10 @@ SPDX-License-Identifier: AGPL-3.0-only
import MkClickerGame from '@/components/MkClickerGame.vue';
import { definePageMetadata } from '@/scripts/page-metadata.js';
definePageMetadata({
definePageMetadata(() => ({
title: '🍪👈',
icon: 'ti ti-cookie',
});
}));
</script>
<style lang="scss" module>

View file

@ -157,10 +157,10 @@ const headerActions = computed(() => clip.value && isOwned.value ? [{
},
}] : null);
definePageMetadata(computed(() => clip.value ? {
title: clip.value.name,
definePageMetadata(() => ({
title: clip.value ? clip.value.name : i18n.ts.clip,
icon: 'ti ti-paperclip',
} : null));
}));
</script>
<style lang="scss" module>

View file

@ -305,10 +305,10 @@ const headerTabs = computed(() => [{
title: i18n.ts.remote,
}]);
definePageMetadata(computed(() => ({
definePageMetadata(() => ({
title: i18n.ts.customEmojis,
icon: 'ti ti-icons',
})));
}));
</script>
<style lang="scss" scoped>

View file

@ -48,8 +48,8 @@ const headerTabs = computed(() => [{
icon: 'ti ti-pencil',
}]);
definePageMetadata(computed(() => ({
definePageMetadata(() => ({
title: i18n.ts._fileViewer.title,
icon: 'ti ti-file',
})));
}));
</script>

View file

@ -22,9 +22,9 @@ const headerActions = computed(() => []);
const headerTabs = computed(() => []);
definePageMetadata(computed(() => ({
definePageMetadata(() => ({
title: folder.value ? folder.value.name : i18n.ts.drive,
icon: 'ti ti-cloud',
hideHeader: true,
})));
}));
</script>

View file

@ -1204,10 +1204,10 @@ onDeactivated(() => {
bgmNodes?.soundSource.stop();
});
definePageMetadata({
definePageMetadata(() => ({
title: i18n.ts.bubbleGame,
icon: 'ti ti-apple',
});
}));
</script>
<style lang="scss" module>

View file

@ -121,10 +121,10 @@ function onGameEnd() {
gameStarted.value = false;
}
definePageMetadata({
definePageMetadata(() => ({
title: i18n.ts.bubbleGame,
icon: 'ti ti-device-gamepad',
});
}));
</script>
<style lang="scss" module>

View file

@ -60,8 +60,8 @@ const headerTabs = computed(() => [{
title: i18n.ts.roles,
}]);
definePageMetadata(computed(() => ({
definePageMetadata(() => ({
title: i18n.ts.explore,
icon: 'ti ti-hash',
})));
}));
</script>

View file

@ -38,10 +38,10 @@ const pagination = {
limit: 10,
};
definePageMetadata({
definePageMetadata(() => ({
title: i18n.ts.favorites,
icon: 'ti ti-star',
});
}));
</script>
<style lang="scss" module>

View file

@ -452,9 +452,7 @@ const headerActions = computed(() => []);
const headerTabs = computed(() => []);
definePageMetadata(computed(() => flash.value ? {
title: i18n.ts._play.edit + ': ' + flash.value.title,
} : {
title: i18n.ts._play.new,
definePageMetadata(() => ({
title: flash.value ? `${i18n.ts._play.edit}: ${flash.value.title}` : i18n.ts._play.new,
}));
</script>

View file

@ -90,8 +90,8 @@ const headerTabs = computed(() => [{
icon: 'ti ti-heart',
}]);
definePageMetadata(computed(() => ({
definePageMetadata(() => ({
title: 'Play',
icon: 'ti ti-player-play',
})));
}));
</script>

View file

@ -205,15 +205,17 @@ const headerActions = computed(() => []);
const headerTabs = computed(() => []);
definePageMetadata(computed(() => flash.value ? {
title: flash.value.title,
avatar: flash.value.user,
path: `/play/${flash.value.id}`,
share: {
title: flash.value.title,
text: flash.value.summary,
},
} : null));
definePageMetadata(() => ({
title: flash.value ? flash.value.title : 'Play',
...flash.value ? {
avatar: flash.value.user,
path: `/play/${flash.value.id}`,
share: {
title: flash.value.title,
text: flash.value.summary,
},
} : {},
}));
</script>
<style lang="scss" module>

View file

@ -69,10 +69,10 @@ const headerActions = computed(() => []);
const headerTabs = computed(() => []);
definePageMetadata(computed(() => ({
definePageMetadata(() => ({
title: i18n.ts.followRequests,
icon: 'ti ti-user-plus',
})));
}));
</script>
<style lang="scss" scoped>

View file

@ -122,11 +122,8 @@ const headerActions = computed(() => []);
const headerTabs = computed(() => []);
definePageMetadata(computed(() => props.postId ? {
title: i18n.ts.edit,
icon: 'ti ti-pencil',
} : {
title: i18n.ts.postToGallery,
definePageMetadata(() => ({
title: props.postId ? i18n.ts.edit : i18n.ts.postToGallery,
icon: 'ti ti-pencil',
}));
</script>

View file

@ -119,10 +119,10 @@ const headerTabs = computed(() => [{
icon: 'ti ti-edit',
}]);
definePageMetadata({
definePageMetadata(() => ({
title: i18n.ts.gallery,
icon: 'ti ti-icons',
});
}));
</script>
<style lang="scss" module>

View file

@ -163,10 +163,12 @@ const headerActions = computed(() => [{
const headerTabs = computed(() => []);
definePageMetadata(computed(() => post.value ? {
title: post.value.title,
avatar: post.value.user,
} : null));
definePageMetadata(() => ({
title: post.value ? post.value.title : i18n.ts.gallery,
...post.value ? {
avatar: post.value.user,
} : {},
}));
</script>
<style lang="scss" scoped>

View file

@ -27,8 +27,8 @@ SPDX-License-Identifier: AGPL-3.0-only
import { i18n } from '@/i18n.js';
import { definePageMetadata } from '@/scripts/page-metadata.js';
definePageMetadata({
definePageMetadata(() => ({
title: 'Misskey Games',
icon: 'ti ti-device-gamepad',
});
}));
</script>

View file

@ -312,10 +312,10 @@ const headerActions = computed(() => []);
const headerTabs = computed(() => []);
definePageMetadata({
definePageMetadata(() => ({
title: i18n.ts._externalResourceInstaller.title,
icon: 'ti ti-download',
});
}));
</script>
<style lang="scss" module>

View file

@ -245,10 +245,10 @@ const headerTabs = computed(() => [{
icon: 'ti ti-code',
}]);
definePageMetadata({
definePageMetadata(() => ({
title: props.host,
icon: 'ti ti-server',
});
}));
</script>
<style lang="scss" scoped>

View file

@ -93,10 +93,10 @@ async function update() {
update();
definePageMetadata({
definePageMetadata(() => ({
title: i18n.ts.invite,
icon: 'ti ti-user-plus',
});
}));
</script>
<style lang="scss" module>

View file

@ -101,10 +101,10 @@ const headerActions = computed(() => []);
const headerTabs = computed(() => []);
definePageMetadata(computed(() => list.value ? {
title: list.value.name,
definePageMetadata(() => ({
title: list.value ? list.value.name : i18n.ts.lists,
icon: 'ti ti-list',
} : null));
}));
</script>
<style lang="scss" module>
.main {

View file

@ -93,10 +93,10 @@ const headerActions = computed(() => []);
const headerTabs = computed(() => []);
definePageMetadata({
definePageMetadata(() => ({
title: 'MiAuth',
icon: 'ti ti-apps',
});
}));
</script>
<style lang="scss" module>

Some files were not shown because too many files have changed in this diff Show more