Merge upstream
This commit is contained in:
commit
bc9acabd6c
65 changed files with 502 additions and 500 deletions
|
@ -33,16 +33,16 @@
|
|||
"generate-api-json": "pnpm build && node ./scripts/generate_api_json.js"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@swc/core-darwin-arm64": "1.10.7",
|
||||
"@swc/core-darwin-x64": "1.10.7",
|
||||
"@swc/core-linux-arm-gnueabihf": "1.10.7",
|
||||
"@swc/core-linux-arm64-gnu": "1.10.7",
|
||||
"@swc/core-linux-arm64-musl": "1.10.7",
|
||||
"@swc/core-linux-x64-gnu": "1.10.7",
|
||||
"@swc/core-linux-x64-musl": "1.10.7",
|
||||
"@swc/core-win32-arm64-msvc": "1.10.7",
|
||||
"@swc/core-win32-ia32-msvc": "1.10.7",
|
||||
"@swc/core-win32-x64-msvc": "1.10.7",
|
||||
"@swc/core-darwin-arm64": "1.10.12",
|
||||
"@swc/core-darwin-x64": "1.10.12",
|
||||
"@swc/core-linux-arm-gnueabihf": "1.10.12",
|
||||
"@swc/core-linux-arm64-gnu": "1.10.12",
|
||||
"@swc/core-linux-arm64-musl": "1.10.12",
|
||||
"@swc/core-linux-x64-gnu": "1.10.12",
|
||||
"@swc/core-linux-x64-musl": "1.10.12",
|
||||
"@swc/core-win32-arm64-msvc": "1.10.12",
|
||||
"@swc/core-win32-ia32-msvc": "1.10.12",
|
||||
"@swc/core-win32-x64-msvc": "1.10.12",
|
||||
"@tensorflow/tfjs": "4.22.0",
|
||||
"@tensorflow/tfjs-node": "4.22.0",
|
||||
"bufferutil": "4.0.9",
|
||||
|
@ -63,11 +63,11 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@authenio/samlify-node-xmllint": "2.0.0",
|
||||
"@aws-sdk/client-s3": "3.729.0",
|
||||
"@aws-sdk/lib-storage": "3.729.0",
|
||||
"@bull-board/api": "6.6.2",
|
||||
"@bull-board/fastify": "6.6.2",
|
||||
"@bull-board/ui": "6.6.2",
|
||||
"@aws-sdk/client-s3": "3.740.0",
|
||||
"@aws-sdk/lib-storage": "3.740.0",
|
||||
"@bull-board/api": "6.7.4",
|
||||
"@bull-board/fastify": "6.7.4",
|
||||
"@bull-board/ui": "6.7.4",
|
||||
"@discordapp/twemoji": "15.1.0",
|
||||
"@elastic/elasticsearch": "8.17.0",
|
||||
"@fastify/accepts": "5.0.2",
|
||||
|
@ -76,21 +76,21 @@
|
|||
"@fastify/express": "4.0.2",
|
||||
"@fastify/formbody": "8.0.2",
|
||||
"@fastify/http-proxy": "11.0.1",
|
||||
"@fastify/multipart": "9.0.2",
|
||||
"@fastify/multipart": "9.0.3",
|
||||
"@fastify/static": "8.0.4",
|
||||
"@fastify/view": "10.0.2",
|
||||
"@misskey-dev/sharp-read-bmp": "1.2.0",
|
||||
"@misskey-dev/summaly": "github:MisskeyIO/summaly#5.1.3",
|
||||
"@napi-rs/canvas": "0.1.65",
|
||||
"@nestjs/common": "10.4.15",
|
||||
"@nestjs/core": "10.4.15",
|
||||
"@nestjs/testing": "10.4.15",
|
||||
"@nestjs/common": "11.0.7",
|
||||
"@nestjs/core": "11.0.7",
|
||||
"@nestjs/testing": "11.0.7",
|
||||
"@peertube/http-signature": "1.7.0",
|
||||
"@simplewebauthn/server": "13.1.0",
|
||||
"@simplewebauthn/server": "13.1.1",
|
||||
"@sinonjs/fake-timers": "11.3.1",
|
||||
"@smithy/node-http-handler": "4.0.2",
|
||||
"@swc/cli": "0.6.0",
|
||||
"@swc/core": "1.10.7",
|
||||
"@swc/core": "1.10.12",
|
||||
"@twemoji/parser": "15.1.1",
|
||||
"accepts": "1.3.8",
|
||||
"ajv": "8.17.1",
|
||||
|
@ -99,7 +99,7 @@
|
|||
"bcryptjs": "2.4.3",
|
||||
"blurhash": "2.0.5",
|
||||
"body-parser": "1.20.3",
|
||||
"bullmq": "5.34.10",
|
||||
"bullmq": "5.39.1",
|
||||
"cacheable-lookup": "7.0.0",
|
||||
"cbor": "10.0.3",
|
||||
"chalk": "5.4.1",
|
||||
|
@ -114,7 +114,7 @@
|
|||
"fastify-http-errors-enhanced": "6.0.1",
|
||||
"fastify-raw-body": "5.0.0",
|
||||
"feed": "4.2.2",
|
||||
"file-type": "20.0.0",
|
||||
"file-type": "20.0.1",
|
||||
"fluent-ffmpeg": "2.1.3",
|
||||
"form-data": "4.0.1",
|
||||
"got": "14.4.5",
|
||||
|
@ -131,7 +131,7 @@
|
|||
"json5": "2.2.3",
|
||||
"jsonld": "8.3.3",
|
||||
"jsrsasign": "11.1.0",
|
||||
"meilisearch": "0.48.0",
|
||||
"meilisearch": "0.48.2",
|
||||
"mfm-js": "0.24.0",
|
||||
"microformats-parser": "2.0.2",
|
||||
"mime-types": "2.1.35",
|
||||
|
@ -142,7 +142,7 @@
|
|||
"nested-property": "4.0.0",
|
||||
"node-fetch": "3.3.2",
|
||||
"node-forge": "1.3.1",
|
||||
"nodemailer": "6.9.16",
|
||||
"nodemailer": "6.10.0",
|
||||
"nsfwjs": "4.2.0",
|
||||
"oauth": "0.10.0",
|
||||
"oauth2orize": "1.12.0",
|
||||
|
@ -191,7 +191,7 @@
|
|||
"devDependencies": {
|
||||
"@jest/globals": "29.7.0",
|
||||
"@misskey-dev/eslint-plugin": "1.0.0",
|
||||
"@nestjs/platform-express": "10.4.15",
|
||||
"@nestjs/platform-express": "11.0.7",
|
||||
"@swc/jest": "0.2.37",
|
||||
"@types/accepts": "1.3.7",
|
||||
"@types/archiver": "6.0.3",
|
||||
|
@ -208,14 +208,14 @@
|
|||
"@types/jsonld": "1.5.15",
|
||||
"@types/jsrsasign": "10.5.15",
|
||||
"@types/mime-types": "2.1.4",
|
||||
"@types/ms": "0.7.34",
|
||||
"@types/node": "22.10.7",
|
||||
"@types/ms": "2.1.0",
|
||||
"@types/node": "22.13.0",
|
||||
"@types/node-forge": "1.3.11",
|
||||
"@types/nodemailer": "6.4.17",
|
||||
"@types/oauth": "0.9.6",
|
||||
"@types/oauth2orize": "1.11.5",
|
||||
"@types/oauth2orize-pkce": "0.1.2",
|
||||
"@types/pg": "8.11.10",
|
||||
"@types/pg": "8.11.11",
|
||||
"@types/psl": "1.1.3",
|
||||
"@types/pug": "2.0.10",
|
||||
"@types/punycode.js": "npm:@types/punycode@2.1.4",
|
||||
|
@ -231,7 +231,7 @@
|
|||
"@types/tmp": "0.2.6",
|
||||
"@types/vary": "1.1.3",
|
||||
"@types/web-push": "3.6.4",
|
||||
"@types/ws": "8.5.13",
|
||||
"@types/ws": "8.5.14",
|
||||
"@typescript-eslint/eslint-plugin": "7.10.0",
|
||||
"@typescript-eslint/parser": "7.10.0",
|
||||
"aws-sdk-client-mock": "4.1.0",
|
||||
|
@ -243,7 +243,7 @@
|
|||
"jest": "29.7.0",
|
||||
"jest-mock": "29.7.0",
|
||||
"nodemon": "3.1.9",
|
||||
"pid-port": "1.0.0",
|
||||
"pid-port": "1.0.2",
|
||||
"simple-oauth2": "5.1.0"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -356,7 +356,7 @@ export class RoleService implements OnApplicationShutdown, OnModuleInit {
|
|||
* 指定ユーザーのバッジロール一覧取得
|
||||
*/
|
||||
@bindThis
|
||||
public async getUserBadgeRoles(userId: MiUser['id']) {
|
||||
public async getUserBadgeRoles(userId: MiUser['id'], publicOnly: boolean) {
|
||||
const now = Date.now();
|
||||
let assigns = await this.roleAssignmentByUserIdCache.fetch(userId, () => this.roleAssignmentsRepository.findBy({ userId }));
|
||||
// 期限切れのロールを除外
|
||||
|
@ -368,12 +368,25 @@ export class RoleService implements OnApplicationShutdown, OnModuleInit {
|
|||
if (badgeCondRoles.length > 0) {
|
||||
const user = roles.some(r => r.target === 'conditional') ? await this.cacheService.findUserById(userId) : null;
|
||||
const matchedBadgeCondRoles = badgeCondRoles.filter(r => this.evalCond(user!, assignedRoles, r.condFormula));
|
||||
return [...assignedBadgeRoles, ...matchedBadgeCondRoles];
|
||||
return this.sortAndMapBadgeRoles([...assignedBadgeRoles, ...matchedBadgeCondRoles], publicOnly);
|
||||
} else {
|
||||
return assignedBadgeRoles;
|
||||
return this.sortAndMapBadgeRoles(assignedBadgeRoles, publicOnly);
|
||||
}
|
||||
}
|
||||
|
||||
@bindThis
|
||||
private sortAndMapBadgeRoles(roles: MiRole[], publicOnly: boolean) {
|
||||
return roles
|
||||
.filter((r) => r.isPublic || !publicOnly)
|
||||
.sort((a, b) => b.displayOrder - a.displayOrder)
|
||||
.map((r) => ({
|
||||
name: r.name,
|
||||
iconUrl: r.iconUrl,
|
||||
displayOrder: r.displayOrder,
|
||||
behavior: r.badgeBehavior ?? undefined,
|
||||
}));
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public async getUserPolicies(userId: MiUser['id'] | null): Promise<RolePolicies> {
|
||||
const meta = await this.metaService.fetch();
|
||||
|
|
|
@ -12,7 +12,7 @@ import type { Packed } from '@/misc/json-schema.js';
|
|||
import { awaitAll } from '@/misc/prelude/await-all.js';
|
||||
import type { MiUser } from '@/models/User.js';
|
||||
import type { MiDriveFile } from '@/models/DriveFile.js';
|
||||
import { appendQuery, query } from '@/misc/prelude/url.js';
|
||||
import { appendQuery, omitHttps, query } from '@/misc/prelude/url.js';
|
||||
import { deepClone } from '@/misc/clone.js';
|
||||
import { bindThis } from '@/decorators.js';
|
||||
import { isMimeImage } from '@/misc/is-mime-image.js';
|
||||
|
@ -77,9 +77,8 @@ export class DriveFileEntityService {
|
|||
@bindThis
|
||||
private getProxiedUrl(url: string, mode?: 'static' | 'avatar'): string {
|
||||
return appendQuery(
|
||||
`${this.config.mediaProxy}/${mode ?? 'image'}.webp`,
|
||||
`${this.config.mediaProxy}/${mode ?? 'image'}/${encodeURIComponent(omitHttps(url))}`,
|
||||
query({
|
||||
url,
|
||||
...(mode ? { [mode]: '1' } : {}),
|
||||
}),
|
||||
);
|
||||
|
|
|
@ -518,16 +518,7 @@ export class UserEntityService implements OnModuleInit {
|
|||
} : undefined) : undefined,
|
||||
emojis: this.customEmojiService.populateEmojis(user.emojis, user.host),
|
||||
onlineStatus: this.getOnlineStatus(user),
|
||||
badgeRoles: this.roleService.getUserBadgeRoles(user.id).then((rs) => rs
|
||||
.filter((r) => r.isPublic || iAmModerator)
|
||||
.sort((a, b) => b.displayOrder - a.displayOrder)
|
||||
.map((r) => ({
|
||||
name: r.name,
|
||||
iconUrl: r.iconUrl,
|
||||
displayOrder: r.displayOrder,
|
||||
behavior: r.badgeBehavior ?? undefined,
|
||||
})),
|
||||
),
|
||||
badgeRoles: this.roleService.getUserBadgeRoles(user.id, !iAmModerator),
|
||||
|
||||
...(isDetailed ? {
|
||||
url: profile?.url,
|
||||
|
|
|
@ -14,10 +14,16 @@ export function query(obj: Record<string, unknown>): string {
|
|||
.reduce((a, [k, v]) => (a[k] = v, a), {} as Record<string, any>);
|
||||
|
||||
return Object.entries(params)
|
||||
.map((e) => `${e[0]}=${encodeURIComponent(e[1])}`)
|
||||
.map((p) => `${p[0]}=${encodeURIComponent(p[1])}`)
|
||||
.join('&');
|
||||
}
|
||||
|
||||
export function appendQuery(url: string, query: string): string {
|
||||
return `${url}${/\?/.test(url) ? url.endsWith('?') ? '' : '&' : '?'}${query}`;
|
||||
}
|
||||
|
||||
export function omitHttps(url: string): string {
|
||||
if (url.startsWith('https://')) return url.slice(8);
|
||||
if (url.startsWith('https%3A%2F%2F')) return url.slice(14);
|
||||
return url;
|
||||
}
|
||||
|
|
|
@ -108,7 +108,7 @@ class MyCustomLogger implements Logger {
|
|||
|
||||
@bindThis
|
||||
public logQuery(query: string, parameters?: any[]) {
|
||||
sqlLogger.info(this.highlight(query));
|
||||
sqlLogger.debug(this.highlight(query));
|
||||
}
|
||||
|
||||
@bindThis
|
||||
|
|
|
@ -26,6 +26,7 @@ import { FileInfoService } from '@/core/FileInfoService.js';
|
|||
import { LoggerService } from '@/core/LoggerService.js';
|
||||
import { bindThis } from '@/decorators.js';
|
||||
import { isMimeImage } from '@/misc/is-mime-image.js';
|
||||
import { appendQuery, omitHttps, query } from '@/misc/prelude/url.js';
|
||||
import { correctFilename } from '@/misc/correct-filename.js';
|
||||
import { handleRequestRedirectToOmitSearch } from '@/misc/fastify-hook-handlers.js';
|
||||
import type { FastifyInstance, FastifyRequest, FastifyReply, FastifyPluginOptions } from 'fastify';
|
||||
|
@ -35,6 +36,16 @@ const _dirname = dirname(_filename);
|
|||
|
||||
const assets = `${_dirname}/../../server/file/assets/`;
|
||||
|
||||
interface TransformQuery {
|
||||
origin?: string;
|
||||
fallback?: string;
|
||||
emoji?: string;
|
||||
avatar?: string;
|
||||
static?: string;
|
||||
preview?: string;
|
||||
badge?: string;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class FileServerService {
|
||||
private logger: Logger;
|
||||
|
@ -87,10 +98,18 @@ export class FileServerService {
|
|||
done();
|
||||
});
|
||||
|
||||
fastify.get<{
|
||||
Params: { type: string; url: string; };
|
||||
Querystring: { url?: string; } & TransformQuery;
|
||||
}>('/proxy/:type/:url', async (request, reply) => {
|
||||
return await this.proxyHandler(request, reply)
|
||||
.catch(err => this.errorHandler(request, reply, err));
|
||||
});
|
||||
|
||||
fastify.get<{
|
||||
Params: { url: string; };
|
||||
Querystring: { url?: string; };
|
||||
}>('/proxy/:url*', async (request, reply) => {
|
||||
Querystring: { url?: string; } & TransformQuery;
|
||||
}>('/proxy/:url', async (request, reply) => {
|
||||
return await this.proxyHandler(request, reply)
|
||||
.catch(err => this.errorHandler(request, reply, err));
|
||||
});
|
||||
|
@ -142,12 +161,15 @@ export class FileServerService {
|
|||
if (isMimeImage(file.mime, 'sharp-convertible-image-with-bmp')) {
|
||||
reply.header('Cache-Control', 'max-age=31536000, immutable');
|
||||
|
||||
const url = new URL(`${this.config.mediaProxy}/static.webp`);
|
||||
url.searchParams.set('url', file.url);
|
||||
url.searchParams.set('static', '1');
|
||||
const url = appendQuery(
|
||||
`${this.config.mediaProxy}/static/${encodeURIComponent(omitHttps(file.url))}`,
|
||||
query({
|
||||
static: '1',
|
||||
}),
|
||||
);
|
||||
|
||||
file.cleanup();
|
||||
return await reply.redirect(url.toString(), 301);
|
||||
return await reply.redirect(url, 301);
|
||||
} else if (file.mime.startsWith('video/')) {
|
||||
const externalThumbnail = this.videoProcessingService.getExternalVideoThumbnailUrl(file.url);
|
||||
if (externalThumbnail) {
|
||||
|
@ -163,11 +185,10 @@ export class FileServerService {
|
|||
if (['image/svg+xml'].includes(file.mime)) {
|
||||
reply.header('Cache-Control', 'max-age=31536000, immutable');
|
||||
|
||||
const url = new URL(`${this.config.mediaProxy}/svg.webp`);
|
||||
url.searchParams.set('url', file.url);
|
||||
const url = `${this.config.mediaProxy}/svg/${encodeURIComponent(omitHttps(file.url))}`;
|
||||
|
||||
file.cleanup();
|
||||
return await reply.redirect(url.toString(), 301);
|
||||
return await reply.redirect(url, 301);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -291,30 +312,43 @@ export class FileServerService {
|
|||
}
|
||||
|
||||
@bindThis
|
||||
private async proxyHandler(request: FastifyRequest<{ Params: { url: string; }; Querystring: { url?: string; }; }>, reply: FastifyReply) {
|
||||
const url = 'url' in request.query ? request.query.url : 'https://' + request.params.url;
|
||||
private async proxyHandler(request: FastifyRequest<{ Params: { type?: string; url: string; }; Querystring: { url?: string; } & TransformQuery; }>, reply: FastifyReply) {
|
||||
let url: string;
|
||||
if ('url' in request.query && request.query.url) {
|
||||
url = request.query.url;
|
||||
} else {
|
||||
url = request.params.url;
|
||||
}
|
||||
|
||||
if (typeof url !== 'string') {
|
||||
// noinspection HttpUrlsUsage
|
||||
if (url
|
||||
&& !url.startsWith('http://')
|
||||
&& !url.startsWith('https://')
|
||||
) {
|
||||
url = 'https://' + url;
|
||||
}
|
||||
|
||||
if (!url) {
|
||||
reply.code(400);
|
||||
return;
|
||||
}
|
||||
|
||||
// アバタークロップなど、どうしてもオリジンである必要がある場合
|
||||
const mustOrigin = 'origin' in request.query;
|
||||
const transformQuery = request.query as TransformQuery;
|
||||
|
||||
if (this.config.externalMediaProxyEnabled && !mustOrigin) {
|
||||
// 外部のメディアプロキシが有効なら、そちらにリダイレクト
|
||||
|
||||
reply.header('Cache-Control', 'public, max-age=259200'); // 3 days
|
||||
|
||||
const url = new URL(`${this.config.mediaProxy}/${request.params.url || ''}`);
|
||||
|
||||
for (const [key, value] of Object.entries(request.query)) {
|
||||
url.searchParams.append(key, value);
|
||||
}
|
||||
const redirectUrl = appendQuery(
|
||||
`${this.config.mediaProxy}/redirect/${encodeURIComponent(omitHttps(url))}`,
|
||||
query(transformQuery as Record<string, string>),
|
||||
);
|
||||
|
||||
return reply.redirect(
|
||||
url.toString(),
|
||||
redirectUrl,
|
||||
301,
|
||||
);
|
||||
}
|
||||
|
@ -344,11 +378,11 @@ export class FileServerService {
|
|||
const isAnimationConvertibleImage = isMimeImage(file.mime, 'sharp-animation-convertible-image-with-bmp');
|
||||
|
||||
if (
|
||||
'emoji' in request.query ||
|
||||
'avatar' in request.query ||
|
||||
'static' in request.query ||
|
||||
'preview' in request.query ||
|
||||
'badge' in request.query
|
||||
'emoji' in transformQuery ||
|
||||
'avatar' in transformQuery ||
|
||||
'static' in transformQuery ||
|
||||
'preview' in transformQuery ||
|
||||
'badge' in transformQuery
|
||||
) {
|
||||
if (!isConvertibleImage) {
|
||||
// 画像でないなら404でお茶を濁す
|
||||
|
@ -357,17 +391,17 @@ export class FileServerService {
|
|||
}
|
||||
|
||||
let image: IImageStreamable | null = null;
|
||||
if ('emoji' in request.query || 'avatar' in request.query) {
|
||||
if (!isAnimationConvertibleImage && !('static' in request.query)) {
|
||||
if ('emoji' in transformQuery || 'avatar' in transformQuery) {
|
||||
if (!isAnimationConvertibleImage && !('static' in transformQuery)) {
|
||||
image = {
|
||||
data: fs.createReadStream(file.path),
|
||||
ext: file.ext,
|
||||
type: file.mime,
|
||||
};
|
||||
} else {
|
||||
const data = (await sharpBmp(file.path, file.mime, { animated: !('static' in request.query) }))
|
||||
const data = (await sharpBmp(file.path, file.mime, { animated: !('static' in transformQuery) }))
|
||||
.resize({
|
||||
height: 'emoji' in request.query ? 128 : 320,
|
||||
height: 'emoji' in transformQuery ? 128 : 320,
|
||||
withoutEnlargement: true,
|
||||
})
|
||||
.webp(webpDefault);
|
||||
|
@ -378,11 +412,11 @@ export class FileServerService {
|
|||
type: 'image/webp',
|
||||
};
|
||||
}
|
||||
} else if ('static' in request.query) {
|
||||
} else if ('static' in transformQuery) {
|
||||
image = this.imageProcessingService.convertSharpToWebpStream(await sharpBmp(file.path, file.mime), 498, 422);
|
||||
} else if ('preview' in request.query) {
|
||||
} else if ('preview' in transformQuery) {
|
||||
image = this.imageProcessingService.convertSharpToWebpStream(await sharpBmp(file.path, file.mime), 200, 200);
|
||||
} else if ('badge' in request.query) {
|
||||
} else if ('badge' in transformQuery) {
|
||||
const mask = (await sharpBmp(file.path, file.mime))
|
||||
.resize(96, 96, {
|
||||
fit: 'contain',
|
||||
|
|
|
@ -19,6 +19,7 @@ import { DI } from '@/di-symbols.js';
|
|||
import type Logger from '@/logger.js';
|
||||
import * as Acct from '@/misc/acct.js';
|
||||
import { genIdenticon } from '@/misc/gen-identicon.js';
|
||||
import { appendQuery, omitHttps, query } from '@/misc/prelude/url.js';
|
||||
import { UserEntityService } from '@/core/entities/UserEntityService.js';
|
||||
import { LoggerService } from '@/core/LoggerService.js';
|
||||
import { bindThis } from '@/decorators.js';
|
||||
|
@ -77,8 +78,9 @@ export class ServerService implements OnApplicationShutdown {
|
|||
@bindThis
|
||||
public async launch(): Promise<void> {
|
||||
const fastify = Fastify({
|
||||
trustProxy: true,
|
||||
logger: false,
|
||||
maxParamLength: 1024,
|
||||
trustProxy: true,
|
||||
});
|
||||
this.#fastify = fastify;
|
||||
|
||||
|
@ -162,22 +164,28 @@ export class ServerService implements OnApplicationShutdown {
|
|||
}
|
||||
}
|
||||
|
||||
let url: URL;
|
||||
let url: string;
|
||||
if ('badge' in request.query) {
|
||||
url = new URL(`${this.config.mediaProxy}/emoji.png`);
|
||||
// || emoji.originalUrl してるのは後方互換性のため(publicUrlはstringなので??はだめ)
|
||||
url.searchParams.set('url', emoji.publicUrl || emoji.originalUrl);
|
||||
url.searchParams.set('badge', '1');
|
||||
url = appendQuery(
|
||||
// || emoji.originalUrl してるのは後方互換性のため(publicUrlはstringなので??はだめ)
|
||||
`${this.config.mediaProxy}/emoji/${encodeURIComponent(omitHttps(emoji.publicUrl || emoji.originalUrl))}`,
|
||||
query({
|
||||
badge: '1',
|
||||
}),
|
||||
);
|
||||
} else {
|
||||
url = new URL(`${this.config.mediaProxy}/emoji.webp`);
|
||||
// || emoji.originalUrl してるのは後方互換性のため(publicUrlはstringなので??はだめ)
|
||||
url.searchParams.set('url', emoji.publicUrl || emoji.originalUrl);
|
||||
url.searchParams.set('emoji', '1');
|
||||
if ('static' in request.query) url.searchParams.set('static', '1');
|
||||
url = appendQuery(
|
||||
// || emoji.originalUrl してるのは後方互換性のため(publicUrlはstringなので??はだめ)
|
||||
`${this.config.mediaProxy}/emoji/${encodeURIComponent(omitHttps(emoji.publicUrl || emoji.originalUrl))}`,
|
||||
query({
|
||||
emoji: '1',
|
||||
...('static' in request.query ? { static: '1' } : {}),
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
return reply.redirect(
|
||||
url.toString(),
|
||||
url,
|
||||
301,
|
||||
);
|
||||
});
|
||||
|
|
|
@ -15,6 +15,7 @@ import { bindThis } from '@/decorators.js';
|
|||
import { CacheService } from '@/core/CacheService.js';
|
||||
import { MiLocalUser } from '@/models/User.js';
|
||||
import { UserService } from '@/core/UserService.js';
|
||||
import { RoleService } from '@/core/RoleService.js';
|
||||
import { ChannelFollowingService } from '@/core/ChannelFollowingService.js';
|
||||
import { AuthenticateService, AuthenticationError } from './AuthenticateService.js';
|
||||
import MainStreamConnection from './stream/Connection.js';
|
||||
|
@ -40,6 +41,7 @@ export class StreamingApiServerService {
|
|||
private channelsService: ChannelsService,
|
||||
private notificationService: NotificationService,
|
||||
private usersService: UserService,
|
||||
private roleService: RoleService,
|
||||
private channelFollowingService: ChannelFollowingService,
|
||||
) {
|
||||
}
|
||||
|
@ -99,6 +101,7 @@ export class StreamingApiServerService {
|
|||
this.noteReadService,
|
||||
this.notificationService,
|
||||
this.cacheService,
|
||||
this.roleService,
|
||||
this.channelFollowingService,
|
||||
user, app,
|
||||
);
|
||||
|
|
|
@ -62,6 +62,8 @@ export const paramDef = {
|
|||
untilId: { type: 'string', format: 'misskey:id' },
|
||||
movedFromId: { type: 'string', format: 'misskey:id', nullable: true },
|
||||
movedToId: { type: 'string', format: 'misskey:id', nullable: true },
|
||||
from: { type: 'string', enum: ['local', 'remote', 'all'], nullable: true },
|
||||
to: { type: 'string', enum: ['local', 'remote', 'all'], nullable: true },
|
||||
},
|
||||
required: [],
|
||||
} as const;
|
||||
|
@ -86,6 +88,28 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
query.andWhere('accountMoveLogs.movedToId = :movedToId', { movedToId: ps.movedToId });
|
||||
}
|
||||
|
||||
if (ps.from != null || ps.to != null) {
|
||||
query
|
||||
.innerJoin('accountMoveLogs.movedFrom', 'movedFrom')
|
||||
.innerJoin('accountMoveLogs.movedTo', 'movedTo');
|
||||
|
||||
if (ps.from === 'local') {
|
||||
query.andWhere('movedFrom.host IS NULL');
|
||||
}
|
||||
|
||||
if (ps.from === 'remote') {
|
||||
query.andWhere('movedFrom.host IS NOT NULL');
|
||||
}
|
||||
|
||||
if (ps.to === 'local') {
|
||||
query.andWhere('movedTo.host IS NULL');
|
||||
}
|
||||
|
||||
if (ps.to === 'remote') {
|
||||
query.andWhere('movedTo.host IS NOT NULL');
|
||||
}
|
||||
}
|
||||
|
||||
const accountMoveLogs = await query.limit(ps.limit).getMany();
|
||||
|
||||
return await this.userAccountMoveLogEntityService.packMany(accountMoveLogs, me);
|
||||
|
|
|
@ -14,6 +14,7 @@ import { CacheService } from '@/core/CacheService.js';
|
|||
import { MiFollowing, MiUserProfile } from '@/models/_.js';
|
||||
import type { StreamEventEmitter, GlobalEvents } from '@/core/GlobalEventService.js';
|
||||
import { ChannelFollowingService } from '@/core/ChannelFollowingService.js';
|
||||
import { RoleService } from '@/core/RoleService.js';
|
||||
import type { ChannelsService } from './ChannelsService.js';
|
||||
import type { EventEmitter } from 'events';
|
||||
import type Channel from './channel.js';
|
||||
|
@ -31,6 +32,7 @@ export default class Connection {
|
|||
private subscribingNotes: any = {};
|
||||
private cachedNotes: Packed<'Note'>[] = [];
|
||||
public userProfile: MiUserProfile | null = null;
|
||||
public isModerator = false;
|
||||
public following: Record<string, Pick<MiFollowing, 'withReplies'> | undefined> = {};
|
||||
public followingChannels: Set<string> = new Set();
|
||||
public userIdsWhoMeMuting: Set<string> = new Set();
|
||||
|
@ -45,6 +47,7 @@ export default class Connection {
|
|||
private noteReadService: NoteReadService,
|
||||
private notificationService: NotificationService,
|
||||
private cacheService: CacheService,
|
||||
private roleService: RoleService,
|
||||
private channelFollowingService: ChannelFollowingService,
|
||||
|
||||
user: MiUser | null | undefined,
|
||||
|
@ -80,6 +83,7 @@ export default class Connection {
|
|||
public async init() {
|
||||
if (this.user != null) {
|
||||
await this.fetch();
|
||||
this.isModerator = await this.roleService.isModerator(this.user);
|
||||
|
||||
if (!this.fetchIntervalId) {
|
||||
this.fetchIntervalId = setInterval(this.fetch, 1000 * 10);
|
||||
|
|
|
@ -30,6 +30,10 @@ export default abstract class Channel {
|
|||
return this.connection.userProfile;
|
||||
}
|
||||
|
||||
protected get iAmModerator() {
|
||||
return this.connection.isModerator;
|
||||
}
|
||||
|
||||
protected get following() {
|
||||
return this.connection.following;
|
||||
}
|
||||
|
|
|
@ -4,8 +4,9 @@
|
|||
*/
|
||||
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
|
||||
import { bindThis } from '@/decorators.js';
|
||||
import { RoleService } from '@/core/RoleService.js';
|
||||
import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
|
||||
import type { GlobalEvents } from '@/core/GlobalEventService.js';
|
||||
import Channel, { type MiChannelService } from '../channel.js';
|
||||
|
||||
|
@ -18,6 +19,7 @@ class AntennaChannel extends Channel {
|
|||
private minimize: boolean;
|
||||
|
||||
constructor(
|
||||
private roleService: RoleService,
|
||||
private noteEntityService: NoteEntityService,
|
||||
|
||||
id: string,
|
||||
|
@ -64,11 +66,14 @@ class AntennaChannel extends Channel {
|
|||
}
|
||||
|
||||
if (this.minimize && ['public', 'home'].includes(note.visibility)) {
|
||||
const badgeRoles = this.iAmModerator ? await this.roleService.getUserBadgeRoles(note.userId, false) : undefined;
|
||||
|
||||
this.send('note', {
|
||||
id: note.id, myReaction: note.myReaction,
|
||||
poll: note.poll?.choices ? { choices: note.poll.choices } : undefined,
|
||||
reply: note.reply?.myReaction ? { myReaction: note.reply.myReaction } : undefined,
|
||||
renote: note.renote?.myReaction ? { myReaction: note.renote.myReaction } : undefined,
|
||||
...(badgeRoles?.length ? { user: { badgeRoles } } : {}),
|
||||
});
|
||||
} else {
|
||||
this.send('note', note);
|
||||
|
@ -92,6 +97,7 @@ export class AntennaChannelService implements MiChannelService<true> {
|
|||
public readonly kind = AntennaChannel.kind;
|
||||
|
||||
constructor(
|
||||
private roleService: RoleService,
|
||||
private noteEntityService: NoteEntityService,
|
||||
) {
|
||||
}
|
||||
|
@ -99,6 +105,7 @@ export class AntennaChannelService implements MiChannelService<true> {
|
|||
@bindThis
|
||||
public create(id: string, connection: Channel['connection']): AntennaChannel {
|
||||
return new AntennaChannel(
|
||||
this.roleService,
|
||||
this.noteEntityService,
|
||||
id,
|
||||
connection,
|
||||
|
|
|
@ -4,9 +4,10 @@
|
|||
*/
|
||||
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import type { Packed } from '@/misc/json-schema.js';
|
||||
import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
|
||||
import { bindThis } from '@/decorators.js';
|
||||
import type { Packed } from '@/misc/json-schema.js';
|
||||
import { RoleService } from '@/core/RoleService.js';
|
||||
import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
|
||||
import { isRenotePacked, isQuotePacked } from '@/misc/is-renote.js';
|
||||
import Channel, { type MiChannelService } from '../channel.js';
|
||||
|
||||
|
@ -18,6 +19,7 @@ class ChannelChannel extends Channel {
|
|||
private minimize: boolean;
|
||||
|
||||
constructor(
|
||||
private roleService: RoleService,
|
||||
private noteEntityService: NoteEntityService,
|
||||
|
||||
id: string,
|
||||
|
@ -70,11 +72,14 @@ class ChannelChannel extends Channel {
|
|||
}
|
||||
|
||||
if (this.minimize && ['public', 'home'].includes(note.visibility)) {
|
||||
const badgeRoles = this.iAmModerator ? await this.roleService.getUserBadgeRoles(note.userId, false) : undefined;
|
||||
|
||||
this.send('note', {
|
||||
id: note.id, myReaction: note.myReaction,
|
||||
poll: note.poll?.choices ? { choices: note.poll.choices } : undefined,
|
||||
reply: note.reply?.myReaction ? { myReaction: note.reply.myReaction } : undefined,
|
||||
renote: note.renote?.myReaction ? { myReaction: note.renote.myReaction } : undefined,
|
||||
...(badgeRoles?.length ? { user: { badgeRoles } } : {}),
|
||||
});
|
||||
} else {
|
||||
this.send('note', note);
|
||||
|
@ -95,6 +100,7 @@ export class ChannelChannelService implements MiChannelService<false> {
|
|||
public readonly kind = ChannelChannel.kind;
|
||||
|
||||
constructor(
|
||||
private roleService: RoleService,
|
||||
private noteEntityService: NoteEntityService,
|
||||
) {
|
||||
}
|
||||
|
@ -102,6 +108,7 @@ export class ChannelChannelService implements MiChannelService<false> {
|
|||
@bindThis
|
||||
public create(id: string, connection: Channel['connection']): ChannelChannel {
|
||||
return new ChannelChannel(
|
||||
this.roleService,
|
||||
this.noteEntityService,
|
||||
id,
|
||||
connection,
|
||||
|
|
|
@ -4,11 +4,11 @@
|
|||
*/
|
||||
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { bindThis } from '@/decorators.js';
|
||||
import type { Packed } from '@/misc/json-schema.js';
|
||||
import { MetaService } from '@/core/MetaService.js';
|
||||
import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
|
||||
import { bindThis } from '@/decorators.js';
|
||||
import { RoleService } from '@/core/RoleService.js';
|
||||
import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
|
||||
import { isRenotePacked, isQuotePacked } from '@/misc/is-renote.js';
|
||||
import Channel, { type MiChannelService } from '../channel.js';
|
||||
|
||||
|
@ -100,11 +100,14 @@ class GlobalTimelineChannel extends Channel {
|
|||
}
|
||||
|
||||
if (this.minimize && ['public', 'home'].includes(note.visibility)) {
|
||||
const badgeRoles = this.iAmModerator ? await this.roleService.getUserBadgeRoles(note.userId, false) : undefined;
|
||||
|
||||
this.send('note', {
|
||||
id: note.id, myReaction: note.myReaction,
|
||||
poll: note.poll?.choices ? { choices: note.poll.choices } : undefined,
|
||||
reply: note.reply?.myReaction ? { myReaction: note.reply.myReaction } : undefined,
|
||||
renote: note.renote?.myReaction ? { myReaction: note.renote.myReaction } : undefined,
|
||||
...(badgeRoles?.length ? { user: { badgeRoles } } : {}),
|
||||
});
|
||||
} else {
|
||||
this.send('note', note);
|
||||
|
|
|
@ -4,9 +4,10 @@
|
|||
*/
|
||||
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import type { Packed } from '@/misc/json-schema.js';
|
||||
import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
|
||||
import { bindThis } from '@/decorators.js';
|
||||
import type { Packed } from '@/misc/json-schema.js';
|
||||
import { RoleService } from '@/core/RoleService.js';
|
||||
import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
|
||||
import { isRenotePacked, isQuotePacked } from '@/misc/is-renote.js';
|
||||
import Channel, { type MiChannelService } from '../channel.js';
|
||||
|
||||
|
@ -20,6 +21,7 @@ class HomeTimelineChannel extends Channel {
|
|||
private minimize: boolean;
|
||||
|
||||
constructor(
|
||||
private roleService: RoleService,
|
||||
private noteEntityService: NoteEntityService,
|
||||
|
||||
id: string,
|
||||
|
@ -101,11 +103,14 @@ class HomeTimelineChannel extends Channel {
|
|||
}
|
||||
|
||||
if (this.minimize && ['public', 'home'].includes(note.visibility)) {
|
||||
const badgeRoles = this.iAmModerator ? await this.roleService.getUserBadgeRoles(note.userId, false) : undefined;
|
||||
|
||||
this.send('note', {
|
||||
id: note.id, myReaction: note.myReaction,
|
||||
poll: note.poll?.choices ? { choices: note.poll.choices } : undefined,
|
||||
reply: note.reply?.myReaction ? { myReaction: note.reply.myReaction } : undefined,
|
||||
renote: note.renote?.myReaction ? { myReaction: note.renote.myReaction } : undefined,
|
||||
...(badgeRoles?.length ? { user: { badgeRoles } } : {}),
|
||||
});
|
||||
} else {
|
||||
this.send('note', note);
|
||||
|
@ -126,6 +131,7 @@ export class HomeTimelineChannelService implements MiChannelService<true> {
|
|||
public readonly kind = HomeTimelineChannel.kind;
|
||||
|
||||
constructor(
|
||||
private roleService: RoleService,
|
||||
private noteEntityService: NoteEntityService,
|
||||
) {
|
||||
}
|
||||
|
@ -133,6 +139,7 @@ export class HomeTimelineChannelService implements MiChannelService<true> {
|
|||
@bindThis
|
||||
public create(id: string, connection: Channel['connection']): HomeTimelineChannel {
|
||||
return new HomeTimelineChannel(
|
||||
this.roleService,
|
||||
this.noteEntityService,
|
||||
id,
|
||||
connection,
|
||||
|
|
|
@ -4,11 +4,11 @@
|
|||
*/
|
||||
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { bindThis } from '@/decorators.js';
|
||||
import type { Packed } from '@/misc/json-schema.js';
|
||||
import { MetaService } from '@/core/MetaService.js';
|
||||
import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
|
||||
import { bindThis } from '@/decorators.js';
|
||||
import { RoleService } from '@/core/RoleService.js';
|
||||
import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
|
||||
import { isRenotePacked, isQuotePacked } from '@/misc/is-renote.js';
|
||||
import Channel, { type MiChannelService } from '../channel.js';
|
||||
|
||||
|
@ -117,11 +117,14 @@ class HybridTimelineChannel extends Channel {
|
|||
}
|
||||
|
||||
if (this.minimize && ['public', 'home'].includes(note.visibility)) {
|
||||
const badgeRoles = this.iAmModerator ? await this.roleService.getUserBadgeRoles(note.userId, false) : undefined;
|
||||
|
||||
this.send('note', {
|
||||
id: note.id, myReaction: note.myReaction,
|
||||
poll: note.poll?.choices ? { choices: note.poll.choices } : undefined,
|
||||
reply: note.reply?.myReaction ? { myReaction: note.reply.myReaction } : undefined,
|
||||
renote: note.renote?.myReaction ? { myReaction: note.renote.myReaction } : undefined,
|
||||
...(badgeRoles?.length ? { user: { badgeRoles } } : {}),
|
||||
});
|
||||
} else {
|
||||
this.send('note', note);
|
||||
|
|
|
@ -4,11 +4,11 @@
|
|||
*/
|
||||
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { bindThis } from '@/decorators.js';
|
||||
import type { Packed } from '@/misc/json-schema.js';
|
||||
import { MetaService } from '@/core/MetaService.js';
|
||||
import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
|
||||
import { bindThis } from '@/decorators.js';
|
||||
import { RoleService } from '@/core/RoleService.js';
|
||||
import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
|
||||
import { isQuotePacked, isRenotePacked } from '@/misc/is-renote.js';
|
||||
import Channel, { type MiChannelService } from '../channel.js';
|
||||
|
||||
|
@ -100,11 +100,14 @@ class LocalTimelineChannel extends Channel {
|
|||
}
|
||||
|
||||
if (this.minimize && ['public', 'home'].includes(note.visibility)) {
|
||||
const badgeRoles = this.iAmModerator ? await this.roleService.getUserBadgeRoles(note.userId, false) : undefined;
|
||||
|
||||
this.send('note', {
|
||||
id: note.id, myReaction: note.myReaction,
|
||||
poll: note.poll?.choices ? { choices: note.poll.choices } : undefined,
|
||||
reply: note.reply?.myReaction ? { myReaction: note.reply.myReaction } : undefined,
|
||||
renote: note.renote?.myReaction ? { myReaction: note.renote.myReaction } : undefined,
|
||||
...(badgeRoles?.length ? { user: { badgeRoles } } : {}),
|
||||
});
|
||||
} else {
|
||||
this.send('note', note);
|
||||
|
|
|
@ -4,9 +4,9 @@
|
|||
*/
|
||||
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
|
||||
import { bindThis } from '@/decorators.js';
|
||||
import { RoleService } from '@/core/RoleService.js';
|
||||
import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
|
||||
import type { GlobalEvents } from '@/core/GlobalEventService.js';
|
||||
import { isRenotePacked, isQuotePacked } from '@/misc/is-renote.js';
|
||||
import Channel, { type MiChannelService } from '../channel.js';
|
||||
|
@ -19,8 +19,8 @@ class RoleTimelineChannel extends Channel {
|
|||
private minimize: boolean;
|
||||
|
||||
constructor(
|
||||
private roleService: RoleService,
|
||||
private noteEntityService: NoteEntityService,
|
||||
private roleservice: RoleService,
|
||||
|
||||
id: string,
|
||||
connection: Channel['connection'],
|
||||
|
@ -42,7 +42,7 @@ class RoleTimelineChannel extends Channel {
|
|||
if (data.type === 'note') {
|
||||
const note = data.body;
|
||||
|
||||
if (!(await this.roleservice.isExplorable({ id: this.roleId }))) {
|
||||
if (!(await this.roleService.isExplorable({ id: this.roleId }))) {
|
||||
return;
|
||||
}
|
||||
if (note.visibility !== 'public') return;
|
||||
|
@ -86,11 +86,14 @@ class RoleTimelineChannel extends Channel {
|
|||
}
|
||||
|
||||
if (this.minimize && ['public', 'home'].includes(note.visibility)) {
|
||||
const badgeRoles = this.iAmModerator ? await this.roleService.getUserBadgeRoles(note.userId, false) : undefined;
|
||||
|
||||
this.send('note', {
|
||||
id: note.id, myReaction: note.myReaction,
|
||||
poll: note.poll?.choices ? { choices: note.poll.choices } : undefined,
|
||||
reply: note.reply?.myReaction ? { myReaction: note.reply.myReaction } : undefined,
|
||||
renote: note.renote?.myReaction ? { myReaction: note.renote.myReaction } : undefined,
|
||||
...(badgeRoles?.length ? { user: { badgeRoles } } : {}),
|
||||
});
|
||||
} else {
|
||||
this.send('note', note);
|
||||
|
@ -114,16 +117,16 @@ export class RoleTimelineChannelService implements MiChannelService<false> {
|
|||
public readonly kind = RoleTimelineChannel.kind;
|
||||
|
||||
constructor(
|
||||
private roleService: RoleService,
|
||||
private noteEntityService: NoteEntityService,
|
||||
private roleservice: RoleService,
|
||||
) {
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public create(id: string, connection: Channel['connection']): RoleTimelineChannel {
|
||||
return new RoleTimelineChannel(
|
||||
this.roleService,
|
||||
this.noteEntityService,
|
||||
this.roleservice,
|
||||
id,
|
||||
connection,
|
||||
);
|
||||
|
|
|
@ -4,11 +4,12 @@
|
|||
*/
|
||||
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import type { MiUserListMembership, UserListMembershipsRepository, UserListsRepository } from '@/models/_.js';
|
||||
import type { Packed } from '@/misc/json-schema.js';
|
||||
import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import { bindThis } from '@/decorators.js';
|
||||
import type { MiUserListMembership, UserListMembershipsRepository, UserListsRepository } from '@/models/_.js';
|
||||
import type { Packed } from '@/misc/json-schema.js';
|
||||
import { RoleService } from '@/core/RoleService.js';
|
||||
import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
|
||||
import { isRenotePacked, isQuotePacked } from '@/misc/is-renote.js';
|
||||
import Channel, { type MiChannelService } from '../channel.js';
|
||||
|
||||
|
@ -26,6 +27,7 @@ class UserListChannel extends Channel {
|
|||
constructor(
|
||||
private userListsRepository: UserListsRepository,
|
||||
private userListMembershipsRepository: UserListMembershipsRepository,
|
||||
private roleService: RoleService,
|
||||
private noteEntityService: NoteEntityService,
|
||||
|
||||
id: string,
|
||||
|
@ -135,11 +137,14 @@ class UserListChannel extends Channel {
|
|||
}
|
||||
|
||||
if (this.minimize && ['public', 'home'].includes(note.visibility)) {
|
||||
const badgeRoles = this.iAmModerator ? await this.roleService.getUserBadgeRoles(note.userId, false) : undefined;
|
||||
|
||||
this.send('note', {
|
||||
id: note.id, myReaction: note.myReaction,
|
||||
poll: note.poll?.choices ? { choices: note.poll.choices } : undefined,
|
||||
reply: note.reply?.myReaction ? { myReaction: note.reply.myReaction } : undefined,
|
||||
renote: note.renote?.myReaction ? { myReaction: note.renote.myReaction } : undefined,
|
||||
...(badgeRoles?.length ? { user: { badgeRoles } } : {}),
|
||||
});
|
||||
} else {
|
||||
this.send('note', note);
|
||||
|
@ -169,6 +174,7 @@ export class UserListChannelService implements MiChannelService<false> {
|
|||
@Inject(DI.userListMembershipsRepository)
|
||||
private userListMembershipsRepository: UserListMembershipsRepository,
|
||||
|
||||
private roleService: RoleService,
|
||||
private noteEntityService: NoteEntityService,
|
||||
) {
|
||||
}
|
||||
|
@ -178,6 +184,7 @@ export class UserListChannelService implements MiChannelService<false> {
|
|||
return new UserListChannel(
|
||||
this.userListsRepository,
|
||||
this.userListMembershipsRepository,
|
||||
this.roleService,
|
||||
this.noteEntityService,
|
||||
id,
|
||||
connection,
|
||||
|
|
|
@ -303,9 +303,12 @@ export class ClientServerService {
|
|||
done();
|
||||
});
|
||||
} else {
|
||||
const configUrl = new URL(this.config.url);
|
||||
const urlOriginWithoutPort = configUrl.origin.replace(/:\d+$/, '');
|
||||
|
||||
const port = (process.env.VITE_PORT ?? '5173');
|
||||
fastify.register(fastifyProxy, {
|
||||
upstream: 'http://localhost:' + port,
|
||||
upstream: urlOriginWithoutPort + ':' + port,
|
||||
prefix: '/vite',
|
||||
rewritePrefix: '/vite',
|
||||
});
|
||||
|
|
|
@ -12,7 +12,7 @@ import type { Config } from '@/config.js';
|
|||
import { MetaService } from '@/core/MetaService.js';
|
||||
import { HttpRequestService } from '@/core/HttpRequestService.js';
|
||||
import type Logger from '@/logger.js';
|
||||
import { query } from '@/misc/prelude/url.js';
|
||||
import { appendQuery, omitHttps, query } from '@/misc/prelude/url.js';
|
||||
import { LoggerService } from '@/core/LoggerService.js';
|
||||
import { bindThis } from '@/decorators.js';
|
||||
import { ApiError } from '@/server/api/error.js';
|
||||
|
@ -36,14 +36,15 @@ export class UrlPreviewService {
|
|||
|
||||
@bindThis
|
||||
private wrap(url?: string | null): string | null {
|
||||
return url != null
|
||||
? url.match(/^https?:\/\//)
|
||||
? `${this.config.mediaProxy}/preview.webp?${query({
|
||||
url,
|
||||
preview: '1',
|
||||
})}`
|
||||
: url
|
||||
: null;
|
||||
if (!url) return null;
|
||||
if (!RegExp(/^https?:\/\//).exec(url)) return url;
|
||||
|
||||
return appendQuery(
|
||||
`${this.config.mediaProxy}/preview/${encodeURIComponent(omitHttps(url))}`,
|
||||
query({
|
||||
preview: '1',
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
@bindThis
|
||||
|
|
|
@ -36,7 +36,8 @@ html
|
|||
link(rel='prefetch' href=infoImageUrl)
|
||||
link(rel='prefetch' href=notFoundImageUrl)
|
||||
//- https://github.com/misskey-dev/misskey/issues/9842
|
||||
link(rel='stylesheet' href=`/assets/tabler-icons.${version}/dist/tabler-icons.min.css`)
|
||||
link(rel='stylesheet' href=`/assets/tabler-icons.${version}/dist/tabler-icons-outline.min.css`)
|
||||
link(rel='stylesheet' href=`/assets/tabler-icons.${version}/dist/tabler-icons-filled.min.css`)
|
||||
link(rel='modulepreload' href=`/vite/${clientEntry.file}`)
|
||||
|
||||
if !config.clientManifestExists
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue