Compare commits

...

19 Commits

Author SHA1 Message Date
tamaina 9494cac6ef
Merge 4c21f88209 into 3954837cfa 2025-10-05 04:47:08 +09:00
renovate[bot] 3954837cfa
fix(deps): update [root] update dependencies [skip ci] (#16576)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-04 15:58:30 +09:00
renovate[bot] 7ea4cad12e
chore(deps): update [misskey-js] update dependencies [skip ci] (#16543)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-04 15:44:08 +09:00
github-actions[bot] d864e9a269 Bump version to 2025.10.0-beta.0 2025-10-04 06:40:01 +00:00
syuilo 4e0434c275
Update CHANGELOG with new features and enhancements 2025-10-04 15:38:05 +09:00
renovate[bot] e2f939080a
fix(deps): update [frontend] update dependencies [ci skip] (#16548)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-04 15:26:26 +09:00
renovate[bot] 6956f44d1f
chore(deps): update [github actions] update dependencies (#16545)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-04 15:18:21 +09:00
renovate[bot] a393d5a87e
fix(deps): update [backend] update dependencies (#16547)
* fix(deps): update [backend] update dependencies

* chore: update typeorm.patch

* fix: handle socket creation failure in HttpRequestServiceAgent

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: anatawa12 <anatawa12@icloud.com>
Co-authored-by: kakkokari-gtyih <67428053+kakkokari-gtyih@users.noreply.github.com>
2025-10-04 15:04:28 +09:00
syuilo 6c634de482
Bump version to 2025.10.0 in CHANGELOG
Updated version number and note for pnpm requirement.
2025-10-04 09:50:58 +09:00
syuilo fc02e0d34d chore(frontend): make enableFolderPageView false by default
see #16553
2025-10-04 08:54:49 +09:00
syuilo cb1a90ddad chore(frontend): improve usability 2025-10-04 08:53:19 +09:00
tamaina 4c21f88209 optimise 2025-09-04 11:30:23 +09:00
tamaina b398347744 log range request 2025-09-04 02:47:13 +09:00
tamaina 507c07dc0d remove 2025-09-04 02:31:36 +09:00
tamaina 1d19237777 ✌️ 2025-09-04 01:58:30 +09:00
tamaina 71613c0053 fix 2025-09-04 01:24:56 +09:00
tamaina dea632654a fix 2025-09-04 01:02:17 +09:00
tamaina 510cc6692f clean up 2025-09-03 23:58:20 +09:00
tamaina 2eef7005c8 enhance(backend): FileServerService: HTTP byte range requestを無視するように (production) 2025-09-03 23:30:48 +09:00
23 changed files with 2496 additions and 2508 deletions

View File

@ -81,7 +81,7 @@ jobs:
cache: 'pnpm'
- run: pnpm i --frozen-lockfile
- name: Restore eslint cache
uses: actions/cache@v4.2.4
uses: actions/cache@v4.3.0
with:
path: ${{ env.eslint-cache-path }}
key: eslint-${{ env.eslint-cache-version }}-${{ matrix.workspace }}-${{ hashFiles('**/pnpm-lock.yaml') }}-${{ github.ref_name }}-${{ github.sha }}

View File

@ -16,7 +16,7 @@ jobs:
# api-artifact
steps:
- name: Download artifact
uses: actions/github-script@v7.0.1
uses: actions/github-script@v7.1.0
with:
script: |
const fs = require('fs');

View File

@ -90,7 +90,7 @@ jobs:
env:
CHROMATIC_PROJECT_TOKEN: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}
- name: Notify that Chromatic detects changes
uses: actions/github-script@v7.0.1
uses: actions/github-script@v7.1.0
if: github.event_name != 'pull_request_target' && steps.chromatic_push.outputs.success == 'false'
with:
github-token: ${{ secrets.GITHUB_TOKEN }}

View File

@ -1,4 +1,4 @@
## 2025.9.1
## 2025.10.0
### NOTE
- pnpm 10.16.0 が必要です
@ -7,6 +7,8 @@
- Feat: 予約投稿ができるようになりました
- デフォルトで作成可能数は1になっています。適宜ロールのポリシーで設定を行ってください。
- Enhance: 広告ごとにセンシティブフラグを設定できるようになりました
- Enhance: 依存関係の更新
- Enhance: 翻訳の更新
### Client
- Feat: アカウントのQRコードを表示・読み取りできるようになりました

View File

@ -1,12 +1,12 @@
{
"name": "misskey",
"version": "2025.10.0-alpha.0",
"version": "2025.10.0-beta.0",
"codename": "nasubi",
"repository": {
"type": "git",
"url": "https://github.com/misskey-dev/misskey.git"
},
"packageManager": "pnpm@10.16.0",
"packageManager": "pnpm@10.17.1",
"workspaces": [
"packages/frontend-shared",
"packages/frontend",
@ -54,30 +54,30 @@
},
"dependencies": {
"cssnano": "7.1.1",
"esbuild": "0.25.9",
"esbuild": "0.25.10",
"execa": "9.6.0",
"fast-glob": "3.3.3",
"glob": "11.0.3",
"ignore-walk": "7.0.0",
"js-yaml": "4.1.0",
"postcss": "8.5.6",
"tar": "7.4.3",
"tar": "7.5.1",
"terser": "5.44.0",
"typescript": "5.9.2"
},
"devDependencies": {
"@misskey-dev/eslint-plugin": "2.1.0",
"@types/js-yaml": "4.0.9",
"@types/node": "22.18.1",
"@typescript-eslint/eslint-plugin": "8.42.0",
"@typescript-eslint/parser": "8.42.0",
"@types/node": "22.18.6",
"@typescript-eslint/eslint-plugin": "8.44.1",
"@typescript-eslint/parser": "8.44.1",
"cross-env": "7.0.3",
"cypress": "14.5.4",
"eslint": "9.35.0",
"globals": "16.3.0",
"eslint": "9.36.0",
"globals": "16.4.0",
"ncp": "2.0.0",
"pnpm": "10.16.0",
"start-server-and-test": "2.1.0"
"pnpm": "10.17.1",
"start-server-and-test": "2.1.2"
},
"optionalDependencies": {
"@tensorflow/tfjs-core": "4.22.0"

View File

@ -39,17 +39,17 @@
},
"optionalDependencies": {
"@swc/core-android-arm64": "1.3.11",
"@swc/core-darwin-arm64": "1.13.5",
"@swc/core-darwin-x64": "1.13.5",
"@swc/core-darwin-arm64": "1.13.19",
"@swc/core-darwin-x64": "1.13.19",
"@swc/core-freebsd-x64": "1.3.11",
"@swc/core-linux-arm-gnueabihf": "1.13.5",
"@swc/core-linux-arm64-gnu": "1.13.5",
"@swc/core-linux-arm64-musl": "1.13.5",
"@swc/core-linux-x64-gnu": "1.13.5",
"@swc/core-linux-x64-musl": "1.13.5",
"@swc/core-win32-arm64-msvc": "1.13.5",
"@swc/core-win32-ia32-msvc": "1.13.5",
"@swc/core-win32-x64-msvc": "1.13.5",
"@swc/core-linux-arm-gnueabihf": "1.13.19",
"@swc/core-linux-arm64-gnu": "1.13.19",
"@swc/core-linux-arm64-musl": "1.13.19",
"@swc/core-linux-x64-gnu": "1.13.19",
"@swc/core-linux-x64-musl": "1.13.19",
"@swc/core-win32-arm64-msvc": "1.13.19",
"@swc/core-win32-ia32-msvc": "1.13.19",
"@swc/core-win32-x64-msvc": "1.13.19",
"@tensorflow/tfjs": "4.22.0",
"@tensorflow/tfjs-node": "4.22.0",
"bufferutil": "4.0.9",
@ -69,8 +69,8 @@
"utf-8-validate": "6.0.5"
},
"dependencies": {
"@aws-sdk/client-s3": "3.883.0",
"@aws-sdk/lib-storage": "3.883.0",
"@aws-sdk/client-s3": "3.896.0",
"@aws-sdk/lib-storage": "3.895.0",
"@discordapp/twemoji": "16.0.1",
"@fastify/accepts": "5.0.2",
"@fastify/cookie": "11.0.2",
@ -82,7 +82,7 @@
"@fastify/view": "10.0.2",
"@misskey-dev/sharp-read-bmp": "1.2.0",
"@misskey-dev/summaly": "5.2.3",
"@napi-rs/canvas": "0.1.79",
"@napi-rs/canvas": "0.1.80",
"@nestjs/common": "11.1.6",
"@nestjs/core": "11.1.6",
"@nestjs/testing": "11.1.6",
@ -103,29 +103,29 @@
"bcryptjs": "2.4.3",
"blurhash": "2.0.5",
"body-parser": "1.20.3",
"bullmq": "5.58.5",
"bullmq": "5.58.8",
"cacheable-lookup": "7.0.0",
"cbor": "9.0.2",
"chalk": "5.6.0",
"chalk-template": "1.1.0",
"chalk": "5.6.2",
"chalk-template": "1.1.2",
"chokidar": "4.0.3",
"cli-highlight": "2.1.11",
"color-convert": "2.0.1",
"content-disposition": "0.5.4",
"date-fns": "2.30.0",
"deep-email-validator": "0.1.21",
"fastify": "5.6.0",
"fastify": "5.6.1",
"fastify-raw-body": "5.0.0",
"feed": "4.2.2",
"file-type": "19.6.0",
"fluent-ffmpeg": "2.1.3",
"form-data": "4.0.4",
"got": "14.4.8",
"got": "14.4.9",
"happy-dom": "16.8.1",
"hpagent": "1.2.0",
"htmlescape": "1.1.1",
"http-link-header": "1.1.3",
"ioredis": "5.7.0",
"ioredis": "5.8.0",
"ip-cidr": "4.0.2",
"ipaddr.js": "2.2.0",
"is-svg": "5.1.0",
@ -135,14 +135,14 @@
"jsonld": "8.3.3",
"jsrsasign": "11.1.0",
"juice": "11.0.1",
"meilisearch": "0.52.0",
"meilisearch": "0.53.0",
"mfm-js": "0.25.0",
"microformats-parser": "2.0.4",
"mime-types": "2.1.35",
"misskey-js": "workspace:*",
"misskey-reversi": "workspace:*",
"ms": "3.0.0-canary.202508261828",
"nanoid": "5.1.5",
"nanoid": "5.1.6",
"nested-property": "4.0.0",
"node-fetch": "3.3.2",
"nodemailer": "6.10.1",
@ -175,12 +175,12 @@
"slacc": "0.0.10",
"strict-event-emitter-types": "2.0.0",
"stringz": "2.1.0",
"systeminformation": "5.27.8",
"systeminformation": "5.27.10",
"tinycolor2": "1.6.0",
"tmp": "0.2.5",
"tsc-alias": "1.8.16",
"tsconfig-paths": "4.2.0",
"typeorm": "0.3.26",
"typeorm": "0.3.27",
"typescript": "5.9.2",
"ulid": "2.4.0",
"vary": "1.1.2",
@ -210,7 +210,7 @@
"@types/jsrsasign": "10.5.15",
"@types/mime-types": "2.1.4",
"@types/ms": "0.7.34",
"@types/node": "22.18.1",
"@types/node": "22.18.6",
"@types/nodemailer": "6.4.19",
"@types/oauth": "0.9.6",
"@types/oauth2orize": "1.11.5",
@ -231,8 +231,8 @@
"@types/vary": "1.1.3",
"@types/web-push": "3.6.4",
"@types/ws": "8.18.1",
"@typescript-eslint/eslint-plugin": "8.42.0",
"@typescript-eslint/parser": "8.42.0",
"@typescript-eslint/eslint-plugin": "8.44.1",
"@typescript-eslint/parser": "8.44.1",
"aws-sdk-client-mock": "4.1.0",
"cross-env": "7.0.3",
"eslint-plugin-import": "2.32.0",

View File

@ -37,8 +37,13 @@ class HttpRequestServiceAgent extends http.Agent {
@bindThis
public createConnection(options: http.ClientRequestArgs, callback?: (err: Error | null, stream: stream.Duplex) => void): stream.Duplex {
const socket = super.createConnection(options, callback)
.on('connect', () => {
const socket = super.createConnection(options, callback);
if (socket == null) {
throw new Error('Failed to create socket');
}
socket.on('connect', () => {
if (socket instanceof net.Socket && process.env.NODE_ENV === 'production') {
const address = socket.remoteAddress;
if (address && ipaddr.isValid(address)) {
@ -48,6 +53,7 @@ class HttpRequestServiceAgent extends http.Agent {
}
}
});
return socket;
}
@ -76,8 +82,13 @@ class HttpsRequestServiceAgent extends https.Agent {
@bindThis
public createConnection(options: http.ClientRequestArgs, callback?: (err: Error | null, stream: stream.Duplex) => void): stream.Duplex {
const socket = super.createConnection(options, callback)
.on('connect', () => {
const socket = super.createConnection(options, callback);
if (socket == null) {
throw new Error('Failed to create socket');
}
socket.on('connect', () => {
if (socket instanceof net.Socket && process.env.NODE_ENV === 'production') {
const address = socket.remoteAddress;
if (address && ipaddr.isValid(address)) {
@ -87,6 +98,7 @@ class HttpsRequestServiceAgent extends https.Agent {
}
}
});
return socket;
}

View File

@ -10,22 +10,40 @@ export type IImage = {
data: Buffer;
ext: string | null;
type: string;
filename?: string;
size?: number;
};
export type IImageStream = {
data: Readable;
ext: string | null;
type: string;
filename?: string;
size?: number;
};
export type IImageSharp = {
data: sharp.Sharp;
ext: string | null;
type: string;
filename?: string;
size?: number;
};
export type IImageStreamable = IImage | IImageStream | IImageSharp;
export function getSizeFromIImage(image: IImageStreamable): number | undefined {
if ('size' in image) {
return image.size;
}
if (image.data instanceof Buffer) {
return image.data.length;
}
return;
}
export const webpDefault: sharp.WebpOptions = {
quality: 77,
alphaQuality: 95,

View File

@ -18,7 +18,7 @@ import { FILE_TYPE_BROWSERSAFE } from '@/const.js';
import { StatusError } from '@/misc/status-error.js';
import type Logger from '@/logger.js';
import { DownloadService } from '@/core/DownloadService.js';
import { IImageStreamable, ImageProcessingService, webpDefault } from '@/core/ImageProcessingService.js';
import { getSizeFromIImage, type IImageStreamable, ImageProcessingService, webpDefault } from '@/core/ImageProcessingService.js';
import { VideoProcessingService } from '@/core/VideoProcessingService.js';
import { InternalStorageService } from '@/core/InternalStorageService.js';
import { contentDisposition } from '@/misc/content-disposition.js';
@ -117,6 +117,49 @@ export class FileServerService {
return;
}
@bindThis
private processFileAndConvertToIImage(file: Awaited<ReturnType<FileServerService['getStreamAndTypeFromUrl']>>, request: FastifyRequest, reply: FastifyReply): IImageStreamable {
if (typeof file !== 'object' || !file) {
throw new Error('Invalid file');
}
if (request.headers.range && 'file' in file && file.size > 0) {
this.logger.info(`Range request for file ${file.file.id} ${file.fileRole}: ${request.headers.range}`);
const range = request.headers.range as string;
const parts = range.replace(/bytes=/, '').split('-');
const start = parseInt(parts[0], 10);
let end = parts[1] ? parseInt(parts[1], 10) : file.size - 1;
if (end > file.size) {
end = file.size - 1;
}
const chunksize = end - start + 1;
reply.header('Content-Range', `bytes ${start}-${end}/${file.size}`);
reply.header('Accept-Ranges', 'bytes');
reply.code(206);
return {
data: fs.createReadStream(file.path, {
start,
end,
}),
ext: file.ext,
type: file.mime,
size: chunksize,
filename: file.filename,
};
}
return {
data: fs.createReadStream(file.path),
ext: file.ext,
type: file.mime,
size: file.size,
filename: file.filename,
};
}
@bindThis
private async sendDriveFile(request: FastifyRequest<{ Params: { key: string; } }>, reply: FastifyReply) {
const key = request.params.key;
@ -135,9 +178,9 @@ export class FileServerService {
}
try {
if (file.state === 'remote') {
let image: IImageStreamable | null = null;
if (file.state === 'remote') {
if (file.fileRole === 'thumbnail') {
if (isMimeImage(file.mime, 'sharp-convertible-image-with-bmp')) {
reply.header('Cache-Control', 'max-age=31536000, immutable');
@ -172,36 +215,7 @@ export class FileServerService {
}
if (!image) {
if (request.headers.range && file.file.size > 0) {
const range = request.headers.range as string;
const parts = range.replace(/bytes=/, '').split('-');
const start = parseInt(parts[0], 10);
let end = parts[1] ? parseInt(parts[1], 10) : file.file.size - 1;
if (end > file.file.size) {
end = file.file.size - 1;
}
const chunksize = end - start + 1;
image = {
data: fs.createReadStream(file.path, {
start,
end,
}),
ext: file.ext,
type: file.mime,
};
reply.header('Content-Range', `bytes ${start}-${end}/${file.file.size}`);
reply.header('Accept-Ranges', 'bytes');
reply.header('Content-Length', chunksize);
reply.code(206);
} else {
image = {
data: fs.createReadStream(file.path),
ext: file.ext,
type: file.mime,
};
}
image = this.processFileAndConvertToIImage(file, request, reply);
}
if ('pipe' in image.data && typeof image.data.pipe === 'function') {
@ -212,78 +226,31 @@ export class FileServerService {
// image.dataがstreamでないなら直ちにcleanup
file.cleanup();
}
reply.header('Content-Type', FILE_TYPE_BROWSERSAFE.includes(image.type) ? image.type : 'application/octet-stream');
reply.header('Content-Length', file.file.size);
reply.header('Cache-Control', 'max-age=31536000, immutable');
reply.header('Content-Disposition',
contentDisposition(
'inline',
correctFilename(file.filename, image.ext),
),
);
return image.data;
}
} else {
if (file.fileRole !== 'original') {
const filename = rename(file.filename, {
suffix: file.fileRole === 'thumbnail' ? '-thumb' : '-web',
extname: file.ext ? `.${file.ext}` : '.unknown',
}).toString();
reply.header('Content-Type', FILE_TYPE_BROWSERSAFE.includes(file.mime) ? file.mime : 'application/octet-stream');
reply.header('Cache-Control', 'max-age=31536000, immutable');
reply.header('Content-Disposition', contentDisposition('inline', filename));
if (request.headers.range && file.file.size > 0) {
const range = request.headers.range as string;
const parts = range.replace(/bytes=/, '').split('-');
const start = parseInt(parts[0], 10);
let end = parts[1] ? parseInt(parts[1], 10) : file.file.size - 1;
if (end > file.file.size) {
end = file.file.size - 1;
}
const chunksize = end - start + 1;
const fileStream = fs.createReadStream(file.path, {
start,
end,
});
reply.header('Content-Range', `bytes ${start}-${end}/${file.file.size}`);
reply.header('Accept-Ranges', 'bytes');
reply.header('Content-Length', chunksize);
reply.code(206);
return fileStream;
}
return fs.createReadStream(file.path);
image = this.processFileAndConvertToIImage(file, request, reply);
image.filename = filename;
} else {
reply.header('Content-Type', FILE_TYPE_BROWSERSAFE.includes(file.file.type) ? file.file.type : 'application/octet-stream');
reply.header('Content-Length', file.file.size);
image = this.processFileAndConvertToIImage(file, request, reply);
}
}
reply.header('Content-Type', FILE_TYPE_BROWSERSAFE.includes(image.type) ? image.type : 'application/octet-stream');
const size = getSizeFromIImage(image);
if (size) reply.header('Content-Length', size);
reply.header('Cache-Control', 'max-age=31536000, immutable');
reply.header('Content-Disposition', contentDisposition('inline', file.filename));
if (request.headers.range && file.file.size > 0) {
const range = request.headers.range as string;
const parts = range.replace(/bytes=/, '').split('-');
const start = parseInt(parts[0], 10);
let end = parts[1] ? parseInt(parts[1], 10) : file.file.size - 1;
if (end > file.file.size) {
end = file.file.size - 1;
}
const chunksize = end - start + 1;
const fileStream = fs.createReadStream(file.path, {
start,
end,
});
reply.header('Content-Range', `bytes ${start}-${end}/${file.file.size}`);
reply.header('Accept-Ranges', 'bytes');
reply.header('Content-Length', chunksize);
reply.code(206);
return fileStream;
}
return fs.createReadStream(file.path);
}
reply.header('Content-Disposition',
contentDisposition(
'inline',
correctFilename(image.filename ?? file.filename, image.ext),
),
);
return image.data;
} catch (e) {
if ('cleanup' in file) file.cleanup();
throw e;
@ -363,17 +330,31 @@ export class FileServerService {
data: fs.createReadStream(file.path),
ext: file.ext,
type: file.mime,
size: file.size,
};
} else {
const data = (await sharpBmp(file.path, file.mime, { animated: !('static' in request.query) }))
} else if (!('static' in request.query)) {
// animated
const data = (await sharpBmp(file.path, file.mime, { animated: true }))
.resize({
height: 'emoji' in request.query ? 128 : 320,
withoutEnlargement: true,
})
.webp(webpDefault);
});
// byte range requestになる可能性があるので、Content-Lengthを送信するためにbufferにする
image = {
data: await data.webp(webpDefault).toBuffer(),
ext: 'webp',
type: 'image/webp',
};
} else {
const data = (await sharpBmp(file.path, file.mime, { animated: false }))
.resize({
height: 'emoji' in request.query ? 128 : 320,
withoutEnlargement: true,
});
image = {
data,
data: data.webp(webpDefault),
ext: 'webp',
type: 'image/webp',
};
@ -420,36 +401,7 @@ export class FileServerService {
}
if (!image) {
if (request.headers.range && file.file && file.file.size > 0) {
const range = request.headers.range as string;
const parts = range.replace(/bytes=/, '').split('-');
const start = parseInt(parts[0], 10);
let end = parts[1] ? parseInt(parts[1], 10) : file.file.size - 1;
if (end > file.file.size) {
end = file.file.size - 1;
}
const chunksize = end - start + 1;
image = {
data: fs.createReadStream(file.path, {
start,
end,
}),
ext: file.ext,
type: file.mime,
};
reply.header('Content-Range', `bytes ${start}-${end}/${file.file.size}`);
reply.header('Accept-Ranges', 'bytes');
reply.header('Content-Length', chunksize);
reply.code(206);
} else {
image = {
data: fs.createReadStream(file.path),
ext: file.ext,
type: file.mime,
};
}
image = this.processFileAndConvertToIImage(file, request, reply);
}
if ('cleanup' in file) {
@ -464,6 +416,8 @@ export class FileServerService {
}
reply.header('Content-Type', image.type);
const size = getSizeFromIImage(image);
if (size) reply.header('Content-Length', size);
reply.header('Cache-Control', 'max-age=31536000, immutable');
reply.header('Content-Disposition',
contentDisposition(
@ -479,12 +433,7 @@ export class FileServerService {
}
@bindThis
private async getStreamAndTypeFromUrl(url: string): Promise<
{ state: 'remote'; fileRole?: 'thumbnail' | 'webpublic' | 'original'; file?: MiDriveFile; mime: string; ext: string | null; path: string; cleanup: () => void; filename: string; }
| { state: 'stored_internal'; fileRole: 'thumbnail' | 'webpublic' | 'original'; file: MiDriveFile; filename: string; mime: string; ext: string | null; path: string; }
| '404'
| '204'
> {
private async getStreamAndTypeFromUrl(url: string): Promise<Awaited<ReturnType<FileServerService['downloadAndDetectTypeFromUrl']>> | Awaited<ReturnType<FileServerService['getFileFromKey']>>> {
if (url.startsWith(`${this.config.url}/files/`)) {
const key = url.replace(`${this.config.url}/files/`, '').split('/').shift();
if (!key) throw new StatusError('Invalid File Key', 400, 'Invalid File Key');
@ -497,17 +446,18 @@ export class FileServerService {
@bindThis
private async downloadAndDetectTypeFromUrl(url: string): Promise<
{ state: 'remote'; mime: string; ext: string | null; path: string; cleanup: () => void; filename: string; }
{ state: 'remote'; mime: string; ext: string | null; size: number; path: string; cleanup: () => void; filename: string; }
> {
const [path, cleanup] = await createTemp();
try {
const { filename } = await this.downloadService.downloadUrl(url, path);
const { mime, ext } = await this.fileInfoService.detectType(path);
const { size } = await fs.promises.stat(path);
return {
state: 'remote',
mime, ext,
mime, ext, size,
path, cleanup,
filename,
};
@ -519,8 +469,8 @@ export class FileServerService {
@bindThis
private async getFileFromKey(key: string): Promise<
{ state: 'remote'; fileRole: 'thumbnail' | 'webpublic' | 'original'; file: MiDriveFile; filename: string; url: string; mime: string; ext: string | null; path: string; cleanup: () => void; }
| { state: 'stored_internal'; fileRole: 'thumbnail' | 'webpublic' | 'original'; file: MiDriveFile; filename: string; mime: string; ext: string | null; path: string; }
{ state: 'remote'; fileRole: 'thumbnail' | 'webpublic' | 'original'; file: MiDriveFile; filename: string; url: string; mime: string; ext: string | null; size: number; path: string; cleanup: () => void; }
| { state: 'stored_internal'; fileRole: 'thumbnail' | 'webpublic' | 'original'; file: MiDriveFile; filename: string; mime: string; ext: string | null; size: number; path: string; }
| '404'
| '204'
> {
@ -539,7 +489,6 @@ export class FileServerService {
if (!file.storedInternal) {
if (!(file.isLink && file.uri)) return '204';
const result = await this.downloadAndDetectTypeFromUrl(file.uri);
file.size = (await fs.promises.stat(result.path)).size; // DB file.sizeは正確とは限らないので
return {
...result,
url: file.uri,
@ -553,16 +502,18 @@ export class FileServerService {
if (isThumbnail || isWebpublic) {
const { mime, ext } = await this.fileInfoService.detectType(path);
const { size } = await fs.promises.stat(path);
return {
state: 'stored_internal',
fileRole: isThumbnail ? 'thumbnail' : 'webpublic',
file,
filename: file.name,
mime, ext,
mime, ext, size,
path,
};
}
const { size } = await fs.promises.stat(path);
return {
state: 'stored_internal',
fileRole: 'original',
@ -571,6 +522,7 @@ export class FileServerService {
// 古いファイルは修正前のmimeを持っているのでできるだけ修正してあげる
mime: this.fileInfoService.fixMime(file.type),
ext: null,
size,
path,
};
}

View File

@ -11,15 +11,15 @@
},
"devDependencies": {
"@types/estree": "1.0.8",
"@types/node": "22.17.0",
"@typescript-eslint/eslint-plugin": "8.38.0",
"@typescript-eslint/parser": "8.38.0",
"rollup": "4.46.2",
"@types/node": "22.18.6",
"@typescript-eslint/eslint-plugin": "8.44.1",
"@typescript-eslint/parser": "8.44.1",
"rollup": "4.52.2",
"typescript": "5.9.2"
},
"dependencies": {
"estree-walker": "3.0.3",
"magic-string": "0.30.17",
"vite": "7.0.7"
"magic-string": "0.30.19",
"vite": "7.1.7"
}
}

View File

@ -16,7 +16,7 @@
"@rollup/pluginutils": "5.3.0",
"@twemoji/parser": "16.0.0",
"@vitejs/plugin-vue": "6.0.1",
"@vue/compiler-sfc": "3.5.21",
"@vue/compiler-sfc": "3.5.22",
"astring": "1.9.0",
"buraha": "0.0.1",
"estree-walker": "3.0.3",
@ -26,47 +26,47 @@
"mfm-js": "0.25.0",
"misskey-js": "workspace:*",
"punycode.js": "2.3.1",
"rollup": "4.50.1",
"sass": "1.92.1",
"shiki": "3.12.2",
"rollup": "4.52.2",
"sass": "1.93.2",
"shiki": "3.13.0",
"tinycolor2": "1.6.0",
"tsc-alias": "1.8.16",
"tsconfig-paths": "4.2.0",
"typescript": "5.9.2",
"uuid": "11.1.0",
"vite": "7.1.5",
"vue": "3.5.21"
"vite": "7.1.7",
"vue": "3.5.22"
},
"devDependencies": {
"@misskey-dev/summaly": "5.2.3",
"@tabler/icons-webfont": "3.34.1",
"@tabler/icons-webfont": "3.35.0",
"@testing-library/vue": "8.1.0",
"@types/estree": "1.0.8",
"@types/micromatch": "4.0.9",
"@types/node": "22.18.1",
"@types/node": "22.18.6",
"@types/punycode.js": "npm:@types/punycode@2.1.4",
"@types/tinycolor2": "1.4.6",
"@types/ws": "8.18.1",
"@typescript-eslint/eslint-plugin": "8.42.0",
"@typescript-eslint/parser": "8.42.0",
"@typescript-eslint/eslint-plugin": "8.44.1",
"@typescript-eslint/parser": "8.44.1",
"@vitest/coverage-v8": "3.2.4",
"@vue/runtime-core": "3.5.21",
"@vue/runtime-core": "3.5.22",
"acorn": "8.15.0",
"cross-env": "10.0.0",
"eslint-plugin-import": "2.32.0",
"eslint-plugin-vue": "10.4.0",
"eslint-plugin-vue": "10.5.0",
"fast-glob": "3.3.3",
"happy-dom": "18.0.1",
"intersection-observer": "0.12.2",
"micromatch": "4.0.8",
"msw": "2.11.1",
"msw": "2.11.3",
"nodemon": "3.1.10",
"prettier": "3.6.2",
"start-server-and-test": "2.1.0",
"tsx": "4.20.5",
"start-server-and-test": "2.1.2",
"tsx": "4.20.6",
"vite-plugin-turbosnap": "1.0.3",
"vue-component-type-helpers": "3.0.6",
"vue-component-type-helpers": "3.0.8",
"vue-eslint-parser": "10.2.0",
"vue-tsc": "3.0.6"
"vue-tsc": "3.0.8"
}
}

View File

@ -21,11 +21,11 @@
"lint": "pnpm typecheck && pnpm eslint"
},
"devDependencies": {
"@types/node": "22.18.1",
"@typescript-eslint/eslint-plugin": "8.42.0",
"@typescript-eslint/parser": "8.42.0",
"esbuild": "0.25.9",
"eslint-plugin-vue": "10.4.0",
"@types/node": "22.18.6",
"@typescript-eslint/eslint-plugin": "8.44.1",
"@typescript-eslint/parser": "8.44.1",
"esbuild": "0.25.10",
"eslint-plugin-vue": "10.5.0",
"nodemon": "3.1.10",
"typescript": "5.9.2",
"vue-eslint-parser": "10.2.0"
@ -35,6 +35,6 @@
],
"dependencies": {
"misskey-js": "workspace:*",
"vue": "3.5.21"
"vue": "3.5.22"
}
}

View File

@ -24,12 +24,12 @@
"@rollup/plugin-json": "6.1.0",
"@rollup/plugin-replace": "6.0.2",
"@rollup/pluginutils": "5.3.0",
"@sentry/vue": "10.10.0",
"@syuilo/aiscript": "1.1.0",
"@sentry/vue": "10.15.0",
"@syuilo/aiscript": "1.1.2",
"@syuilo/aiscript-0-19-0": "npm:@syuilo/aiscript@^0.19.0",
"@twemoji/parser": "16.0.0",
"@vitejs/plugin-vue": "6.0.1",
"@vue/compiler-sfc": "3.5.21",
"@vue/compiler-sfc": "3.5.22",
"aiscript-vscode": "github:aiscript-dev/aiscript-vscode#v0.1.15",
"analytics": "0.8.19",
"astring": "1.9.0",
@ -41,7 +41,7 @@
"chartjs-chart-matrix": "3.0.0",
"chartjs-plugin-gradient": "0.6.1",
"chartjs-plugin-zoom": "2.2.0",
"chromatic": "13.1.4",
"chromatic": "13.2.1",
"compare-versions": "6.1.1",
"cropperjs": "2.0.1",
"date-fns": "4.1.0",
@ -52,12 +52,12 @@
"icons-subsetter": "workspace:*",
"idb-keyval": "6.2.2",
"insert-text-at-cursor": "0.3.0",
"ios-haptics": "0.1.0",
"ios-haptics": "0.1.4",
"is-file-animated": "1.0.2",
"json5": "2.2.3",
"magic-string": "0.30.18",
"magic-string": "0.30.19",
"matter-js": "0.20.0",
"mediabunny": "1.15.1",
"mediabunny": "1.21.0",
"mfm-js": "0.25.0",
"misskey-bubble-game": "workspace:*",
"misskey-js": "workspace:*",
@ -66,10 +66,10 @@
"punycode.js": "2.3.1",
"qr-code-styling": "1.9.2",
"qr-scanner": "1.4.2",
"rollup": "4.50.1",
"rollup": "4.52.2",
"sanitize-html": "2.17.0",
"sass": "1.92.1",
"shiki": "3.12.2",
"sass": "1.93.2",
"shiki": "3.13.0",
"strict-event-emitter-types": "2.0.0",
"textarea-caret": "3.1.0",
"three": "0.180.0",
@ -79,8 +79,8 @@
"tsconfig-paths": "4.2.0",
"typescript": "5.9.2",
"v-code-diff": "1.13.1",
"vite": "7.1.5",
"vue": "3.5.21",
"vite": "7.1.7",
"vue": "3.5.22",
"vuedraggable": "next",
"wanakana": "5.3.1"
},
@ -88,7 +88,7 @@
"@misskey-dev/summaly": "5.2.3",
"@storybook/addon-essentials": "8.6.14",
"@storybook/addon-interactions": "8.6.14",
"@storybook/addon-links": "9.1.5",
"@storybook/addon-links": "9.1.8",
"@storybook/addon-mdx-gfm": "8.6.14",
"@storybook/addon-storysource": "8.6.14",
"@storybook/blocks": "8.6.14",
@ -96,57 +96,57 @@
"@storybook/core-events": "8.6.14",
"@storybook/manager-api": "8.6.14",
"@storybook/preview-api": "8.6.14",
"@storybook/react": "9.1.5",
"@storybook/react-vite": "9.1.5",
"@storybook/react": "9.1.8",
"@storybook/react-vite": "9.1.8",
"@storybook/test": "8.6.14",
"@storybook/theming": "8.6.14",
"@storybook/types": "8.6.14",
"@storybook/vue3": "9.1.5",
"@storybook/vue3-vite": "9.1.5",
"@tabler/icons-webfont": "3.34.1",
"@storybook/vue3": "9.1.8",
"@storybook/vue3-vite": "9.1.8",
"@tabler/icons-webfont": "3.35.0",
"@testing-library/vue": "8.1.0",
"@types/canvas-confetti": "1.9.0",
"@types/estree": "1.0.8",
"@types/matter-js": "0.20.0",
"@types/matter-js": "0.20.2",
"@types/micromatch": "4.0.9",
"@types/node": "22.18.1",
"@types/node": "22.18.6",
"@types/punycode.js": "npm:@types/punycode@2.1.4",
"@types/sanitize-html": "2.16.0",
"@types/seedrandom": "3.0.8",
"@types/throttle-debounce": "5.0.2",
"@types/tinycolor2": "1.4.6",
"@types/ws": "8.18.1",
"@typescript-eslint/eslint-plugin": "8.42.0",
"@typescript-eslint/parser": "8.42.0",
"@typescript-eslint/eslint-plugin": "8.44.1",
"@typescript-eslint/parser": "8.44.1",
"@vitest/coverage-v8": "3.2.4",
"@vue/compiler-core": "3.5.21",
"@vue/runtime-core": "3.5.21",
"@vue/compiler-core": "3.5.22",
"@vue/runtime-core": "3.5.22",
"acorn": "8.15.0",
"cross-env": "10.0.0",
"cypress": "14.5.4",
"eslint-plugin-import": "2.32.0",
"eslint-plugin-vue": "10.4.0",
"eslint-plugin-vue": "10.5.0",
"fast-glob": "3.3.3",
"happy-dom": "18.0.1",
"intersection-observer": "0.12.2",
"micromatch": "4.0.8",
"minimatch": "10.0.3",
"msw": "2.11.1",
"msw": "2.11.3",
"msw-storybook-addon": "2.0.5",
"nodemon": "3.1.10",
"prettier": "3.6.2",
"react": "19.1.1",
"react-dom": "19.1.1",
"seedrandom": "3.0.5",
"start-server-and-test": "2.1.0",
"storybook": "9.1.5",
"start-server-and-test": "2.1.2",
"storybook": "9.1.8",
"storybook-addon-misskey-theme": "github:misskey-dev/storybook-addon-misskey-theme",
"tsx": "4.20.5",
"tsx": "4.20.6",
"vite-plugin-turbosnap": "1.0.3",
"vitest": "3.2.4",
"vitest-fetch-mock": "0.4.5",
"vue-component-type-helpers": "3.0.6",
"vue-component-type-helpers": "3.0.8",
"vue-eslint-parser": "10.2.0",
"vue-tsc": "3.0.6"
"vue-tsc": "3.0.8"
}
}

View File

@ -948,6 +948,7 @@ watch([
chatShowSenderName,
useStickyIcons,
enableHighQualityImagePlaceholders,
disableShowingAnimatedImages,
keepScreenOn,
contextMenu,
fontSize,
@ -958,6 +959,8 @@ watch([
enablePullToRefresh,
reduceAnimation,
showAvailableReactionsFirstInNote,
animatedMfm,
advancedMfm,
], () => {
suggestReload();
});

View File

@ -511,7 +511,7 @@ export const PREF_DEF = definePreferences({
default: false,
},
'experimental.enableFolderPageView': {
default: true,
default: false,
},
'experimental.enableHapticFeedback': {
default: false,

View File

@ -11,16 +11,16 @@
"lint": "pnpm typecheck && pnpm eslint"
},
"devDependencies": {
"@types/node": "22.18.1",
"@types/node": "22.18.6",
"@types/wawoff2": "1.0.2",
"@typescript-eslint/eslint-plugin": "8.42.0",
"@typescript-eslint/parser": "8.42.0"
"@typescript-eslint/eslint-plugin": "8.44.1",
"@typescript-eslint/parser": "8.44.1"
},
"dependencies": {
"@tabler/icons-webfont": "3.34.1",
"harfbuzzjs": "0.4.11",
"@tabler/icons-webfont": "3.35.0",
"harfbuzzjs": "0.4.12",
"tiny-glob": "0.2.9",
"tsx": "4.20.5",
"tsx": "4.20.6",
"typescript": "5.9.2",
"wawoff2": "2.0.1"
},

View File

@ -22,15 +22,15 @@
"lint": "pnpm typecheck && pnpm eslint"
},
"devDependencies": {
"@types/matter-js": "0.20.0",
"@types/matter-js": "0.20.2",
"@types/seedrandom": "3.0.8",
"@types/node": "22.18.1",
"@typescript-eslint/eslint-plugin": "8.42.0",
"@typescript-eslint/parser": "8.42.0",
"@types/node": "22.18.6",
"@typescript-eslint/eslint-plugin": "8.44.1",
"@typescript-eslint/parser": "8.44.1",
"nodemon": "3.1.10",
"execa": "9.6.0",
"typescript": "5.9.2",
"esbuild": "0.25.9",
"esbuild": "0.25.10",
"glob": "11.0.3"
},
"files": [

View File

@ -8,13 +8,13 @@
},
"devDependencies": {
"@readme/openapi-parser": "5.0.1",
"@types/node": "22.18.1",
"@typescript-eslint/eslint-plugin": "8.42.0",
"@typescript-eslint/parser": "8.42.0",
"@types/node": "22.18.6",
"@typescript-eslint/eslint-plugin": "8.44.1",
"@typescript-eslint/parser": "8.44.1",
"openapi-types": "12.1.3",
"openapi-typescript": "7.9.1",
"ts-case-convert": "2.1.0",
"tsx": "4.20.5",
"tsx": "4.20.6",
"typescript": "5.9.2"
},
"files": [

View File

@ -1,7 +1,7 @@
{
"type": "module",
"name": "misskey-js",
"version": "2025.10.0-alpha.0",
"version": "2025.10.0-beta.0",
"description": "Misskey SDK for JavaScript",
"license": "MIT",
"main": "./built/index.js",
@ -35,12 +35,12 @@
"directory": "packages/misskey-js"
},
"devDependencies": {
"@microsoft/api-extractor": "7.52.11",
"@types/node": "22.18.1",
"@typescript-eslint/eslint-plugin": "8.42.0",
"@typescript-eslint/parser": "8.42.0",
"@microsoft/api-extractor": "7.52.13",
"@types/node": "22.18.6",
"@typescript-eslint/eslint-plugin": "8.44.1",
"@typescript-eslint/parser": "8.44.1",
"@vitest/coverage-v8": "3.2.4",
"esbuild": "0.25.9",
"esbuild": "0.25.10",
"execa": "9.6.0",
"glob": "11.0.3",
"ncp": "2.0.0",

View File

@ -22,13 +22,13 @@
"lint": "pnpm typecheck && pnpm eslint"
},
"devDependencies": {
"@types/node": "22.18.1",
"@typescript-eslint/eslint-plugin": "8.42.0",
"@typescript-eslint/parser": "8.42.0",
"@types/node": "22.18.6",
"@typescript-eslint/eslint-plugin": "8.44.1",
"@typescript-eslint/parser": "8.44.1",
"execa": "9.6.0",
"nodemon": "3.1.10",
"typescript": "5.9.2",
"esbuild": "0.25.9",
"esbuild": "0.25.10",
"glob": "11.0.3"
},
"files": [

View File

@ -9,12 +9,12 @@
"lint": "pnpm typecheck && pnpm eslint"
},
"dependencies": {
"esbuild": "0.25.9",
"esbuild": "0.25.10",
"idb-keyval": "6.2.2",
"misskey-js": "workspace:*"
},
"devDependencies": {
"@typescript-eslint/parser": "8.42.0",
"@typescript-eslint/parser": "8.44.1",
"@typescript/lib-webworker": "npm:@types/serviceworker@0.0.74",
"eslint-plugin-import": "2.32.0",
"nodemon": "3.1.10",

View File

@ -1,8 +1,8 @@
diff --git a/driver/postgres/PostgresDriver.js b/driver/postgres/PostgresDriver.js
index 278f29c1f3deec4939bb4ed90e6edae167f704e0..9a84c3098dda915d6c33e24d925a8fa09af9095e 100644
index e13b903c73b71113bb529552e59fb4ce0ca8af0c..50de6a60120ece7ebf49009eac588a5313343f39 100644
--- a/driver/postgres/PostgresDriver.js
+++ b/driver/postgres/PostgresDriver.js
@@ -785,10 +785,10 @@ class PostgresDriver {
@@ -819,10 +819,10 @@ class PostgresDriver {
const tableColumnDefault = typeof tableColumn.default === "string"
? JSON.parse(tableColumn.default.substring(1, tableColumn.default.length - 1))
: tableColumn.default;

File diff suppressed because it is too large Load Diff