Merge branch 'develop' into removed-note-metadata

This commit is contained in:
anatawa12 2025-08-11 05:28:51 +09:00 committed by GitHub
commit d9758b2183
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
29 changed files with 1283 additions and 1968 deletions

View File

@ -25,7 +25,7 @@ jobs:
cp ./compose_example.yml ./compose.yml cp ./compose_example.yml ./compose.yml
- run: | - run: |
docker compose up -d web docker compose up -d web
docker tag "$(docker compose images web | awk 'OFS=":" {print $4}' | tail -n +2)" misskey-web:latest docker tag "$(docker compose images --format json web | jq -r '.[] | .ID')" misskey-web:latest
- run: | - run: |
cmd="dockle --exit-code 1 misskey-web:latest ${image_name}" cmd="dockle --exit-code 1 misskey-web:latest ${image_name}"
echo "> ${cmd}" echo "> ${cmd}"

View File

@ -17,6 +17,7 @@
- Enhance: Unicode 15.1 および 16.0 に収録されている絵文字に対応 - Enhance: Unicode 15.1 および 16.0 に収録されている絵文字に対応
- Enhance: acctに `.` が入っているユーザーのメンションに対応 - Enhance: acctに `.` が入っているユーザーのメンションに対応
- Fix: Unicode絵文字に隣接する異体字セレクタ`U+FE0F`)が絵文字として認識される問題を修正 - Fix: Unicode絵文字に隣接する異体字セレクタ`U+FE0F`)が絵文字として認識される問題を修正
- Enhance: ユーザー検索をロールポリシーで制限できるように
### Client ### Client
- Feat: AiScriptが1.0に更新されました - Feat: AiScriptが1.0に更新されました

8
locales/index.d.ts vendored
View File

@ -4386,6 +4386,10 @@ export interface Locale extends ILocale {
* *
*/ */
"notesSearchNotAvailable": string; "notesSearchNotAvailable": string;
/**
*
*/
"usersSearchNotAvailable": string;
/** /**
* *
*/ */
@ -7799,6 +7803,10 @@ export interface Locale extends ILocale {
* *
*/ */
"canSearchNotes": string; "canSearchNotes": string;
/**
*
*/
"canSearchUsers": string;
/** /**
* *
*/ */

View File

@ -1092,6 +1092,7 @@ prohibitedWordsDescription2: "スペースで区切るとAND指定になり、
hiddenTags: "非表示ハッシュタグ" hiddenTags: "非表示ハッシュタグ"
hiddenTagsDescription: "設定したタグをトレンドに表示させないようにします。改行で区切って複数設定できます。" hiddenTagsDescription: "設定したタグをトレンドに表示させないようにします。改行で区切って複数設定できます。"
notesSearchNotAvailable: "ノート検索は利用できません。" notesSearchNotAvailable: "ノート検索は利用できません。"
usersSearchNotAvailable: "ユーザー検索は利用できません。"
license: "ライセンス" license: "ライセンス"
unfavoriteConfirm: "お気に入り解除しますか?" unfavoriteConfirm: "お気に入り解除しますか?"
myClips: "自分のクリップ" myClips: "自分のクリップ"
@ -2020,6 +2021,7 @@ _role:
descriptionOfRateLimitFactor: "小さいほど制限が緩和され、大きいほど制限が強化されます。" descriptionOfRateLimitFactor: "小さいほど制限が緩和され、大きいほど制限が強化されます。"
canHideAds: "広告の非表示" canHideAds: "広告の非表示"
canSearchNotes: "ノート検索の利用" canSearchNotes: "ノート検索の利用"
canSearchUsers: "ユーザー検索の利用"
canUseTranslator: "翻訳機能の利用" canUseTranslator: "翻訳機能の利用"
avatarDecorationLimit: "アイコンデコレーションの最大取付個数" avatarDecorationLimit: "アイコンデコレーションの最大取付個数"
canImportAntennas: "アンテナのインポートを許可" canImportAntennas: "アンテナのインポートを許可"

View File

@ -1,6 +1,6 @@
{ {
"name": "misskey", "name": "misskey",
"version": "2025.8.0-alpha.7", "version": "2025.8.0-alpha.9",
"codename": "nasubi", "codename": "nasubi",
"repository": { "repository": {
"type": "git", "type": "git",

View File

@ -38,17 +38,17 @@
}, },
"optionalDependencies": { "optionalDependencies": {
"@swc/core-android-arm64": "1.3.11", "@swc/core-android-arm64": "1.3.11",
"@swc/core-darwin-arm64": "1.12.0", "@swc/core-darwin-arm64": "1.13.3",
"@swc/core-darwin-x64": "1.12.0", "@swc/core-darwin-x64": "1.13.3",
"@swc/core-freebsd-x64": "1.3.11", "@swc/core-freebsd-x64": "1.3.11",
"@swc/core-linux-arm-gnueabihf": "1.12.0", "@swc/core-linux-arm-gnueabihf": "1.13.3",
"@swc/core-linux-arm64-gnu": "1.12.0", "@swc/core-linux-arm64-gnu": "1.13.3",
"@swc/core-linux-arm64-musl": "1.12.0", "@swc/core-linux-arm64-musl": "1.13.3",
"@swc/core-linux-x64-gnu": "1.12.0", "@swc/core-linux-x64-gnu": "1.13.3",
"@swc/core-linux-x64-musl": "1.12.0", "@swc/core-linux-x64-musl": "1.13.3",
"@swc/core-win32-arm64-msvc": "1.12.0", "@swc/core-win32-arm64-msvc": "1.13.3",
"@swc/core-win32-ia32-msvc": "1.12.0", "@swc/core-win32-ia32-msvc": "1.13.3",
"@swc/core-win32-x64-msvc": "1.12.0", "@swc/core-win32-x64-msvc": "1.13.3",
"@tensorflow/tfjs": "4.22.0", "@tensorflow/tfjs": "4.22.0",
"@tensorflow/tfjs-node": "4.22.0", "@tensorflow/tfjs-node": "4.22.0",
"bufferutil": "4.0.9", "bufferutil": "4.0.9",
@ -68,8 +68,8 @@
"utf-8-validate": "6.0.5" "utf-8-validate": "6.0.5"
}, },
"dependencies": { "dependencies": {
"@aws-sdk/client-s3": "3.826.0", "@aws-sdk/client-s3": "3.864.0",
"@aws-sdk/lib-storage": "3.826.0", "@aws-sdk/lib-storage": "3.864.0",
"@discordapp/twemoji": "16.0.1", "@discordapp/twemoji": "16.0.1",
"@fastify/accepts": "5.0.2", "@fastify/accepts": "5.0.2",
"@fastify/cookie": "11.0.2", "@fastify/cookie": "11.0.2",
@ -80,19 +80,19 @@
"@fastify/static": "8.2.0", "@fastify/static": "8.2.0",
"@fastify/view": "10.0.2", "@fastify/view": "10.0.2",
"@misskey-dev/sharp-read-bmp": "1.2.0", "@misskey-dev/sharp-read-bmp": "1.2.0",
"@misskey-dev/summaly": "5.2.1", "@misskey-dev/summaly": "5.2.3",
"@napi-rs/canvas": "0.1.71", "@napi-rs/canvas": "0.1.77",
"@nestjs/common": "11.1.3", "@nestjs/common": "11.1.6",
"@nestjs/core": "11.1.3", "@nestjs/core": "11.1.6",
"@nestjs/testing": "11.1.3", "@nestjs/testing": "11.1.6",
"@peertube/http-signature": "1.7.0", "@peertube/http-signature": "1.7.0",
"@sentry/node": "8.55.0", "@sentry/node": "8.55.0",
"@sentry/profiling-node": "8.55.0", "@sentry/profiling-node": "8.55.0",
"@simplewebauthn/server": "12.0.0", "@simplewebauthn/server": "12.0.0",
"@sinonjs/fake-timers": "11.3.1", "@sinonjs/fake-timers": "11.3.1",
"@smithy/node-http-handler": "2.5.0", "@smithy/node-http-handler": "2.5.0",
"@swc/cli": "0.7.7", "@swc/cli": "0.7.8",
"@swc/core": "1.12.0", "@swc/core": "1.13.3",
"@twemoji/parser": "16.0.0", "@twemoji/parser": "16.0.0",
"@types/redis-info": "3.0.3", "@types/redis-info": "3.0.3",
"accepts": "1.3.8", "accepts": "1.3.8",
@ -102,10 +102,10 @@
"bcryptjs": "2.4.3", "bcryptjs": "2.4.3",
"blurhash": "2.0.5", "blurhash": "2.0.5",
"body-parser": "1.20.3", "body-parser": "1.20.3",
"bullmq": "5.53.2", "bullmq": "5.56.9",
"cacheable-lookup": "7.0.0", "cacheable-lookup": "7.0.0",
"cbor": "9.0.2", "cbor": "9.0.2",
"chalk": "5.4.1", "chalk": "5.5.0",
"chalk-template": "1.1.0", "chalk-template": "1.1.0",
"chokidar": "4.0.3", "chokidar": "4.0.3",
"cli-highlight": "2.1.11", "cli-highlight": "2.1.11",
@ -113,18 +113,18 @@
"content-disposition": "0.5.4", "content-disposition": "0.5.4",
"date-fns": "2.30.0", "date-fns": "2.30.0",
"deep-email-validator": "0.1.21", "deep-email-validator": "0.1.21",
"fastify": "5.3.3", "fastify": "5.4.0",
"fastify-raw-body": "5.0.0", "fastify-raw-body": "5.0.0",
"feed": "4.2.2", "feed": "4.2.2",
"file-type": "19.6.0", "file-type": "19.6.0",
"fluent-ffmpeg": "2.1.3", "fluent-ffmpeg": "2.1.3",
"form-data": "4.0.3", "form-data": "4.0.4",
"got": "14.4.7", "got": "14.4.7",
"happy-dom": "16.8.1", "happy-dom": "16.8.1",
"hpagent": "1.2.0", "hpagent": "1.2.0",
"htmlescape": "1.1.1", "htmlescape": "1.1.1",
"http-link-header": "1.1.3", "http-link-header": "1.1.3",
"ioredis": "5.6.1", "ioredis": "5.7.0",
"ip-cidr": "4.0.2", "ip-cidr": "4.0.2",
"ipaddr.js": "2.2.0", "ipaddr.js": "2.2.0",
"is-svg": "5.1.0", "is-svg": "5.1.0",
@ -136,7 +136,7 @@
"juice": "11.0.1", "juice": "11.0.1",
"meilisearch": "0.51.0", "meilisearch": "0.51.0",
"mfm-js": "0.25.0", "mfm-js": "0.25.0",
"microformats-parser": "2.0.3", "microformats-parser": "2.0.4",
"mime-types": "2.1.35", "mime-types": "2.1.35",
"misskey-js": "workspace:*", "misskey-js": "workspace:*",
"misskey-reversi": "workspace:*", "misskey-reversi": "workspace:*",
@ -152,7 +152,7 @@
"os-utils": "0.0.14", "os-utils": "0.0.14",
"otpauth": "9.4.0", "otpauth": "9.4.0",
"parse5": "7.3.0", "parse5": "7.3.0",
"pg": "8.16.0", "pg": "8.16.3",
"pkce-challenge": "4.1.0", "pkce-challenge": "4.1.0",
"probe-image-size": "7.2.3", "probe-image-size": "7.2.3",
"promise-limit": "2.7.0", "promise-limit": "2.7.0",
@ -174,25 +174,25 @@
"slacc": "0.0.10", "slacc": "0.0.10",
"strict-event-emitter-types": "2.0.0", "strict-event-emitter-types": "2.0.0",
"stringz": "2.1.0", "stringz": "2.1.0",
"systeminformation": "5.27.1", "systeminformation": "5.27.7",
"tinycolor2": "1.6.0", "tinycolor2": "1.6.0",
"tmp": "0.2.3", "tmp": "0.2.3",
"tsc-alias": "1.8.16", "tsc-alias": "1.8.16",
"tsconfig-paths": "4.2.0", "tsconfig-paths": "4.2.0",
"typeorm": "0.3.24", "typeorm": "0.3.25",
"typescript": "5.8.3", "typescript": "5.9.2",
"ulid": "2.4.0", "ulid": "2.4.0",
"vary": "1.1.2", "vary": "1.1.2",
"web-push": "3.6.7", "web-push": "3.6.7",
"ws": "8.18.2", "ws": "8.18.3",
"xev": "3.0.2" "xev": "3.0.2"
}, },
"devDependencies": { "devDependencies": {
"@jest/globals": "29.7.0", "@jest/globals": "29.7.0",
"@nestjs/platform-express": "10.4.19", "@nestjs/platform-express": "10.4.20",
"@sentry/vue": "9.28.0", "@sentry/vue": "9.45.0",
"@simplewebauthn/types": "12.0.0", "@simplewebauthn/types": "12.0.0",
"@swc/jest": "0.2.38", "@swc/jest": "0.2.39",
"@types/accepts": "1.3.7", "@types/accepts": "1.3.7",
"@types/archiver": "6.0.3", "@types/archiver": "6.0.3",
"@types/bcryptjs": "2.4.6", "@types/bcryptjs": "2.4.6",
@ -209,12 +209,12 @@
"@types/jsrsasign": "10.5.15", "@types/jsrsasign": "10.5.15",
"@types/mime-types": "2.1.4", "@types/mime-types": "2.1.4",
"@types/ms": "0.7.34", "@types/ms": "0.7.34",
"@types/node": "22.15.31", "@types/node": "22.17.1",
"@types/nodemailer": "6.4.17", "@types/nodemailer": "6.4.17",
"@types/oauth": "0.9.6", "@types/oauth": "0.9.6",
"@types/oauth2orize": "1.11.5", "@types/oauth2orize": "1.11.5",
"@types/oauth2orize-pkce": "0.1.2", "@types/oauth2orize-pkce": "0.1.2",
"@types/pg": "8.15.4", "@types/pg": "8.15.5",
"@types/pug": "2.0.10", "@types/pug": "2.0.10",
"@types/qrcode": "1.5.5", "@types/qrcode": "1.5.5",
"@types/random-seed": "0.3.5", "@types/random-seed": "0.3.5",
@ -230,11 +230,11 @@
"@types/vary": "1.1.3", "@types/vary": "1.1.3",
"@types/web-push": "3.6.4", "@types/web-push": "3.6.4",
"@types/ws": "8.18.1", "@types/ws": "8.18.1",
"@typescript-eslint/eslint-plugin": "8.34.0", "@typescript-eslint/eslint-plugin": "8.39.0",
"@typescript-eslint/parser": "8.34.0", "@typescript-eslint/parser": "8.39.0",
"aws-sdk-client-mock": "4.1.0", "aws-sdk-client-mock": "4.1.0",
"cross-env": "7.0.3", "cross-env": "7.0.3",
"eslint-plugin-import": "2.31.0", "eslint-plugin-import": "2.32.0",
"execa": "8.0.1", "execa": "8.0.1",
"fkill": "9.0.0", "fkill": "9.0.0",
"jest": "29.7.0", "jest": "29.7.0",
@ -242,6 +242,6 @@
"nodemon": "3.1.10", "nodemon": "3.1.10",
"pid-port": "1.0.2", "pid-port": "1.0.2",
"simple-oauth2": "5.1.0", "simple-oauth2": "5.1.0",
"supertest": "7.1.1" "supertest": "7.1.4"
} }
} }

View File

@ -6,6 +6,7 @@
import * as http from 'node:http'; import * as http from 'node:http';
import * as https from 'node:https'; import * as https from 'node:https';
import * as net from 'node:net'; import * as net from 'node:net';
import * as stream from 'node:stream';
import ipaddr from 'ipaddr.js'; import ipaddr from 'ipaddr.js';
import CacheableLookup from 'cacheable-lookup'; import CacheableLookup from 'cacheable-lookup';
import fetch from 'node-fetch'; import fetch from 'node-fetch';
@ -26,12 +27,6 @@ export type HttpRequestSendOptions = {
validators?: ((res: Response) => void)[]; validators?: ((res: Response) => void)[];
}; };
declare module 'node:http' {
interface Agent {
createConnection(options: net.NetConnectOpts, callback?: (err: unknown, stream: net.Socket) => void): net.Socket;
}
}
class HttpRequestServiceAgent extends http.Agent { class HttpRequestServiceAgent extends http.Agent {
constructor( constructor(
private config: Config, private config: Config,
@ -41,11 +36,11 @@ class HttpRequestServiceAgent extends http.Agent {
} }
@bindThis @bindThis
public createConnection(options: net.NetConnectOpts, callback?: (err: unknown, stream: net.Socket) => void): net.Socket { public createConnection(options: http.ClientRequestArgs, callback?: (err: Error | null, stream: stream.Duplex) => void): stream.Duplex {
const socket = super.createConnection(options, callback) const socket = super.createConnection(options, callback)
.on('connect', () => { .on('connect', () => {
const address = socket.remoteAddress; if (socket instanceof net.Socket && process.env.NODE_ENV === 'production') {
if (process.env.NODE_ENV === 'production') { const address = socket.remoteAddress;
if (address && ipaddr.isValid(address)) { if (address && ipaddr.isValid(address)) {
if (this.isPrivateIp(address)) { if (this.isPrivateIp(address)) {
socket.destroy(new Error(`Blocked address: ${address}`)); socket.destroy(new Error(`Blocked address: ${address}`));
@ -80,11 +75,11 @@ class HttpsRequestServiceAgent extends https.Agent {
} }
@bindThis @bindThis
public createConnection(options: net.NetConnectOpts, callback?: (err: unknown, stream: net.Socket) => void): net.Socket { public createConnection(options: http.ClientRequestArgs, callback?: (err: Error | null, stream: stream.Duplex) => void): stream.Duplex {
const socket = super.createConnection(options, callback) const socket = super.createConnection(options, callback)
.on('connect', () => { .on('connect', () => {
const address = socket.remoteAddress; if (socket instanceof net.Socket && process.env.NODE_ENV === 'production') {
if (process.env.NODE_ENV === 'production') { const address = socket.remoteAddress;
if (address && ipaddr.isValid(address)) { if (address && ipaddr.isValid(address)) {
if (this.isPrivateIp(address)) { if (this.isPrivateIp(address)) {
socket.destroy(new Error(`Blocked address: ${address}`)); socket.destroy(new Error(`Blocked address: ${address}`));

View File

@ -103,6 +103,7 @@ export class QueueService {
for (const def of REPEATABLE_SYSTEM_JOB_DEF) { for (const def of REPEATABLE_SYSTEM_JOB_DEF) {
this.systemQueue.upsertJobScheduler(def.name, { this.systemQueue.upsertJobScheduler(def.name, {
pattern: def.pattern, pattern: def.pattern,
immediately: false,
}, { }, {
name: def.name, name: def.name,
opts: { opts: {

View File

@ -43,6 +43,7 @@ export type RolePolicies = {
canManageCustomEmojis: boolean; canManageCustomEmojis: boolean;
canManageAvatarDecorations: boolean; canManageAvatarDecorations: boolean;
canSearchNotes: boolean; canSearchNotes: boolean;
canSearchUsers: boolean;
canUseTranslator: boolean; canUseTranslator: boolean;
canHideAds: boolean; canHideAds: boolean;
driveCapacityMb: number; driveCapacityMb: number;
@ -82,6 +83,7 @@ export const DEFAULT_POLICIES: RolePolicies = {
canManageCustomEmojis: false, canManageCustomEmojis: false,
canManageAvatarDecorations: false, canManageAvatarDecorations: false,
canSearchNotes: false, canSearchNotes: false,
canSearchUsers: true,
canUseTranslator: true, canUseTranslator: true,
canHideAds: false, canHideAds: false,
driveCapacityMb: 100, driveCapacityMb: 100,
@ -402,6 +404,7 @@ export class RoleService implements OnApplicationShutdown, OnModuleInit {
canManageCustomEmojis: calc('canManageCustomEmojis', vs => vs.some(v => v === true)), canManageCustomEmojis: calc('canManageCustomEmojis', vs => vs.some(v => v === true)),
canManageAvatarDecorations: calc('canManageAvatarDecorations', vs => vs.some(v => v === true)), canManageAvatarDecorations: calc('canManageAvatarDecorations', vs => vs.some(v => v === true)),
canSearchNotes: calc('canSearchNotes', vs => vs.some(v => v === true)), canSearchNotes: calc('canSearchNotes', vs => vs.some(v => v === true)),
canSearchUsers: calc('canSearchUsers', vs => vs.some(v => v === true)),
canUseTranslator: calc('canUseTranslator', vs => vs.some(v => v === true)), canUseTranslator: calc('canUseTranslator', vs => vs.some(v => v === true)),
canHideAds: calc('canHideAds', vs => vs.some(v => v === true)), canHideAds: calc('canHideAds', vs => vs.some(v => v === true)),
driveCapacityMb: calc('driveCapacityMb', vs => Math.max(...vs)), driveCapacityMb: calc('driveCapacityMb', vs => Math.max(...vs)),

View File

@ -212,6 +212,10 @@ export const packedRolePoliciesSchema = {
type: 'boolean', type: 'boolean',
optional: false, nullable: false, optional: false, nullable: false,
}, },
canSearchUsers: {
type: 'boolean',
optional: false, nullable: false,
},
canUseTranslator: { canUseTranslator: {
type: 'boolean', type: 'boolean',
optional: false, nullable: false, optional: false, nullable: false,

View File

@ -13,6 +13,7 @@ export const meta = {
tags: ['users'], tags: ['users'],
requireCredential: false, requireCredential: false,
requiredRolePolicy: 'canSearchUsers',
description: 'Search for users.', description: 'Search for users.',

View File

@ -32,7 +32,6 @@ export default class Connection {
public subscriber: StreamEventEmitter; public subscriber: StreamEventEmitter;
private channels: Channel[] = []; private channels: Channel[] = [];
private subscribingNotes: Partial<Record<string, number>> = {}; private subscribingNotes: Partial<Record<string, number>> = {};
private cachedNotes: Packed<'Note'>[] = [];
public userProfile: MiUserProfile | null = null; public userProfile: MiUserProfile | null = null;
public following: Record<string, Pick<MiFollowing, 'withReplies'> | undefined> = {}; public following: Record<string, Pick<MiFollowing, 'withReplies'> | undefined> = {};
public followingChannels: Set<string> = new Set(); public followingChannels: Set<string> = new Set();
@ -132,26 +131,6 @@ export default class Connection {
this.sendMessageToWs(data.type, data.body); this.sendMessageToWs(data.type, data.body);
} }
@bindThis
public cacheNote(note: Packed<'Note'>) {
const add = (note: Packed<'Note'>) => {
const existIndex = this.cachedNotes.findIndex(n => n.id === note.id);
if (existIndex > -1) {
this.cachedNotes[existIndex] = note;
return;
}
this.cachedNotes.unshift(note);
if (this.cachedNotes.length > 32) {
this.cachedNotes.splice(32);
}
};
add(note);
if (note.reply) add(note.reply);
if (note.renote) add(note.renote);
}
@bindThis @bindThis
private onReadNotification(payload: JsonValue | undefined) { private onReadNotification(payload: JsonValue | undefined) {
this.notificationService.readAllNotification(this.user!.id); this.notificationService.readAllNotification(this.user!.id);

View File

@ -43,8 +43,6 @@ class AntennaChannel extends Channel {
if (this.isNoteMutedOrBlocked(note)) return; if (this.isNoteMutedOrBlocked(note)) return;
this.connection.cacheNote(note);
this.send('note', note); this.send('note', note);
} else { } else {
this.send(data.type, data.body); this.send(data.type, data.body);

View File

@ -49,8 +49,6 @@ class ChannelChannel extends Channel {
} }
} }
this.connection.cacheNote(note);
this.send('note', note); this.send('note', note);
} }

View File

@ -65,8 +65,6 @@ class GlobalTimelineChannel extends Channel {
} }
} }
this.connection.cacheNote(note);
this.send('note', note); this.send('note', note);
} }

View File

@ -53,8 +53,6 @@ class HashtagChannel extends Channel {
} }
} }
this.connection.cacheNote(note);
this.send('note', note); this.send('note', note);
} }

View File

@ -86,8 +86,6 @@ class HomeTimelineChannel extends Channel {
} }
} }
this.connection.cacheNote(note);
this.send('note', note); this.send('note', note);
} }

View File

@ -100,8 +100,6 @@ class HybridTimelineChannel extends Channel {
} }
} }
this.connection.cacheNote(note);
this.send('note', note); this.send('note', note);
} }

View File

@ -75,8 +75,6 @@ class LocalTimelineChannel extends Channel {
} }
} }
this.connection.cacheNote(note);
this.send('note', note); this.send('note', note);
} }

View File

@ -39,7 +39,6 @@ class MainChannel extends Channel {
const note = await this.noteEntityService.pack(data.body.note.id, this.user, { const note = await this.noteEntityService.pack(data.body.note.id, this.user, {
detail: true, detail: true,
}); });
this.connection.cacheNote(note);
data.body.note = note; data.body.note = note;
} }
break; break;
@ -52,7 +51,6 @@ class MainChannel extends Channel {
const note = await this.noteEntityService.pack(data.body.id, this.user, { const note = await this.noteEntityService.pack(data.body.id, this.user, {
detail: true, detail: true,
}); });
this.connection.cacheNote(note);
data.body = note; data.body = note;
} }
break; break;

View File

@ -118,8 +118,6 @@ class UserListChannel extends Channel {
} }
} }
this.connection.cacheNote(note);
this.send('note', note); this.send('note', note);
} }

View File

@ -346,6 +346,26 @@ SPDX-License-Identifier: AGPL-3.0-only
</div> </div>
</MkFolder> </MkFolder>
<MkFolder v-if="matchQuery([i18n.ts._role._options.canSearchUsers, 'canSearchUsers'])">
<template #label>{{ i18n.ts._role._options.canSearchUsers }}</template>
<template #suffix>
<span v-if="role.policies.canSearchUsers.useDefault" :class="$style.useDefaultLabel">{{ i18n.ts._role.useBaseValue }}</span>
<span v-else>{{ role.policies.canSearchUsers.value ? i18n.ts.yes : i18n.ts.no }}</span>
<span :class="$style.priorityIndicator"><i :class="getPriorityIcon(role.policies.canSearchUsers)"></i></span>
</template>
<div class="_gaps">
<MkSwitch v-model="role.policies.canSearchUsers.useDefault" :readonly="readonly">
<template #label>{{ i18n.ts._role.useBaseValue }}</template>
</MkSwitch>
<MkSwitch v-model="role.policies.canSearchUsers.value" :disabled="role.policies.canSearchUsers.useDefault" :readonly="readonly">
<template #label>{{ i18n.ts.enable }}</template>
</MkSwitch>
<MkRange v-model="role.policies.canSearchUsers.priority" :min="0" :max="2" :step="1" easing :textConverter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''">
<template #label>{{ i18n.ts._role.priority }}</template>
</MkRange>
</div>
</MkFolder>
<MkFolder v-if="matchQuery([i18n.ts._role._options.canUseTranslator, 'canUseTranslator'])"> <MkFolder v-if="matchQuery([i18n.ts._role._options.canUseTranslator, 'canUseTranslator'])">
<template #label>{{ i18n.ts._role._options.canUseTranslator }}</template> <template #label>{{ i18n.ts._role._options.canUseTranslator }}</template>
<template #suffix> <template #suffix>

View File

@ -122,6 +122,14 @@ SPDX-License-Identifier: AGPL-3.0-only
</MkSwitch> </MkSwitch>
</MkFolder> </MkFolder>
<MkFolder v-if="matchQuery([i18n.ts._role._options.canSearchUsers, 'canSearchUsers'])">
<template #label>{{ i18n.ts._role._options.canSearchUsers }}</template>
<template #suffix>{{ policies.canSearchUsers ? i18n.ts.yes : i18n.ts.no }}</template>
<MkSwitch v-model="policies.canSearchUsers">
<template #label>{{ i18n.ts.enable }}</template>
</MkSwitch>
</MkFolder>
<MkFolder v-if="matchQuery([i18n.ts._role._options.canUseTranslator, 'canUseTranslator'])"> <MkFolder v-if="matchQuery([i18n.ts._role._options.canUseTranslator, 'canUseTranslator'])">
<template #label>{{ i18n.ts._role._options.canUseTranslator }}</template> <template #label>{{ i18n.ts._role._options.canUseTranslator }}</template>
<template #suffix>{{ policies.canUseTranslator ? i18n.ts.yes : i18n.ts.no }}</template> <template #suffix>{{ policies.canUseTranslator ? i18n.ts.yes : i18n.ts.no }}</template>

View File

@ -15,16 +15,22 @@ SPDX-License-Identifier: AGPL-3.0-only
</div> </div>
<div v-else-if="tab === 'user'" class="_spacer" style="--MI_SPACER-w: 800px;"> <div v-else-if="tab === 'user'" class="_spacer" style="--MI_SPACER-w: 800px;">
<XUser v-bind="props"/> <div v-if="usersSearchAvailable">
<XUser v-bind="props"/>
</div>
<div v-else>
<MkInfo warn>{{ i18n.ts.usersSearchNotAvailable }}</MkInfo>
</div>
</div> </div>
</PageWithHeader> </PageWithHeader>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { computed, defineAsyncComponent, ref, toRef } from 'vue'; import { computed, defineAsyncComponent, ref, toRef } from 'vue';
import { $i } from '@/i.js';
import { i18n } from '@/i18n.js'; import { i18n } from '@/i18n.js';
import { definePage } from '@/page.js'; import { definePage } from '@/page.js';
import { notesSearchAvailable } from '@/utility/check-permissions.js'; import { notesSearchAvailable, usersSearchAvailable } from '@/utility/check-permissions.js';
import MkInfo from '@/components/MkInfo.vue'; import MkInfo from '@/components/MkInfo.vue';
const props = withDefaults(defineProps<{ const props = withDefaults(defineProps<{

View File

@ -17,3 +17,11 @@ export const notesSearchAvailable = (
export const canSearchNonLocalNotes = ( export const canSearchNonLocalNotes = (
instance.noteSearchableScope === 'global' instance.noteSearchableScope === 'global'
); );
export const usersSearchAvailable = (
// FIXME: instance.policies would be null in Vitest
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
($i == null && instance.policies != null && instance.policies.canSearchUsers) ||
($i != null && $i.policies.canSearchUsers) ||
false
);

View File

@ -7,15 +7,15 @@
"generate": "tsx src/generator.ts && eslint ./built/**/*.ts --fix" "generate": "tsx src/generator.ts && eslint ./built/**/*.ts --fix"
}, },
"devDependencies": { "devDependencies": {
"@readme/openapi-parser": "5.0.0", "@readme/openapi-parser": "5.0.1",
"@types/node": "22.16.4", "@types/node": "22.17.1",
"@typescript-eslint/eslint-plugin": "8.37.0", "@typescript-eslint/eslint-plugin": "8.39.0",
"@typescript-eslint/parser": "8.37.0", "@typescript-eslint/parser": "8.39.0",
"openapi-types": "12.1.3", "openapi-types": "12.1.3",
"openapi-typescript": "7.8.0", "openapi-typescript": "7.8.0",
"ts-case-convert": "2.1.0", "ts-case-convert": "2.1.0",
"tsx": "4.20.3", "tsx": "4.20.3",
"typescript": "5.8.3" "typescript": "5.9.2"
}, },
"files": [ "files": [
"built" "built"

View File

@ -1,7 +1,7 @@
{ {
"type": "module", "type": "module",
"name": "misskey-js", "name": "misskey-js",
"version": "2025.8.0-alpha.7", "version": "2025.8.0-alpha.9",
"description": "Misskey SDK for JavaScript", "description": "Misskey SDK for JavaScript",
"license": "MIT", "license": "MIT",
"main": "./built/index.js", "main": "./built/index.js",
@ -35,18 +35,18 @@
"directory": "packages/misskey-js" "directory": "packages/misskey-js"
}, },
"devDependencies": { "devDependencies": {
"@microsoft/api-extractor": "7.52.8", "@microsoft/api-extractor": "7.52.10",
"@types/node": "22.16.4", "@types/node": "22.17.1",
"@typescript-eslint/eslint-plugin": "8.37.0", "@typescript-eslint/eslint-plugin": "8.39.0",
"@typescript-eslint/parser": "8.37.0", "@typescript-eslint/parser": "8.39.0",
"@vitest/coverage-v8": "3.2.4", "@vitest/coverage-v8": "3.2.4",
"esbuild": "0.25.6", "esbuild": "0.25.8",
"execa": "9.6.0", "execa": "9.6.0",
"glob": "11.0.3", "glob": "11.0.3",
"ncp": "2.0.0", "ncp": "2.0.0",
"nodemon": "3.1.10", "nodemon": "3.1.10",
"tsd": "0.32.0", "tsd": "0.33.0",
"typescript": "5.8.3", "typescript": "5.9.2",
"vitest": "3.2.4", "vitest": "3.2.4",
"vitest-websocket-mock": "0.5.0" "vitest-websocket-mock": "0.5.0"
}, },

View File

@ -5211,6 +5211,7 @@ export type components = {
canManageCustomEmojis: boolean; canManageCustomEmojis: boolean;
canManageAvatarDecorations: boolean; canManageAvatarDecorations: boolean;
canSearchNotes: boolean; canSearchNotes: boolean;
canSearchUsers: boolean;
canUseTranslator: boolean; canUseTranslator: boolean;
canHideAds: boolean; canHideAds: boolean;
driveCapacityMb: number; driveCapacityMb: number;

File diff suppressed because it is too large Load Diff