summaryrefslogtreecommitdiff
path: root/packages/backend/src/core
diff options
context:
space:
mode:
authorHazelnoot <acomputerdog@gmail.com>2025-04-29 15:37:43 -0400
committerHazelnoot <acomputerdog@gmail.com>2025-04-29 15:54:11 -0400
commit9c301fa5aac7e339a2b41feab8d0d247a60f50aa (patch)
tree26e1423620a2811a5e9372bcee6858851d9fad3e /packages/backend/src/core
parentalign `docker_example.yml` with `example.yml` (diff)
parentchore: follow up on fixing Chromatic CI diff strategy (#15912) (diff)
downloadsharkey-9c301fa5aac7e339a2b41feab8d0d247a60f50aa.tar.gz
sharkey-9c301fa5aac7e339a2b41feab8d0d247a60f50aa.tar.bz2
sharkey-9c301fa5aac7e339a2b41feab8d0d247a60f50aa.zip
Merge branch 'misskey-develop' into merge/2025-03-24
# Conflicts: # .github/workflows/api-misskey-js.yml # .github/workflows/changelog-check.yml # .github/workflows/check-misskey-js-autogen.yml # .github/workflows/get-api-diff.yml # .github/workflows/lint.yml # .github/workflows/locale.yml # .github/workflows/on-release-created.yml # .github/workflows/storybook.yml # .github/workflows/test-backend.yml # .github/workflows/test-federation.yml # .github/workflows/test-frontend.yml # .github/workflows/test-misskey-js.yml # .github/workflows/test-production.yml # .github/workflows/validate-api-json.yml # package.json # packages/backend/package.json # packages/backend/src/server/api/ApiCallService.ts # packages/backend/src/server/api/endpoints/drive/files/create.ts # packages/frontend-shared/js/url.ts # packages/frontend/package.json # packages/frontend/src/components/MkFileCaptionEditWindow.vue # packages/frontend/src/components/MkInfo.vue # packages/frontend/src/components/MkLink.vue # packages/frontend/src/components/MkNote.vue # packages/frontend/src/components/MkNotes.vue # packages/frontend/src/components/MkPageWindow.vue # packages/frontend/src/components/MkReactionsViewer.vue # packages/frontend/src/components/MkTimeline.vue # packages/frontend/src/components/MkUrlPreview.vue # packages/frontend/src/components/MkUserPopup.vue # packages/frontend/src/components/global/MkPageHeader.vue # packages/frontend/src/components/global/MkUrl.vue # packages/frontend/src/components/global/PageWithHeader.vue # packages/frontend/src/pages/about-misskey.vue # packages/frontend/src/pages/announcements.vue # packages/frontend/src/pages/antenna-timeline.vue # packages/frontend/src/pages/channel.vue # packages/frontend/src/pages/instance-info.vue # packages/frontend/src/pages/note.vue # packages/frontend/src/pages/page.vue # packages/frontend/src/pages/role.vue # packages/frontend/src/pages/tag.vue # packages/frontend/src/pages/timeline.vue # packages/frontend/src/pages/user-list-timeline.vue # packages/frontend/src/pages/user/followers.vue # packages/frontend/src/pages/user/following.vue # packages/frontend/src/pages/user/home.vue # packages/frontend/src/pages/user/index.vue # packages/frontend/src/ui/deck.vue # packages/misskey-js/generator/package.json # pnpm-lock.yaml # scripts/changelog-checker/package-lock.json # scripts/changelog-checker/package.json
Diffstat (limited to 'packages/backend/src/core')
-rw-r--r--packages/backend/src/core/DriveService.ts7
-rw-r--r--packages/backend/src/core/FanoutTimelineEndpointService.ts20
-rw-r--r--packages/backend/src/core/ImageProcessingService.ts1
-rw-r--r--packages/backend/src/core/QueryService.ts38
-rw-r--r--packages/backend/src/core/RoleService.ts3
-rw-r--r--packages/backend/src/core/SearchService.ts12
6 files changed, 77 insertions, 4 deletions
diff --git a/packages/backend/src/core/DriveService.ts b/packages/backend/src/core/DriveService.ts
index 4be1b0e41b..bb76b680a4 100644
--- a/packages/backend/src/core/DriveService.ts
+++ b/packages/backend/src/core/DriveService.ts
@@ -515,9 +515,16 @@ export class DriveService {
const policies = await this.roleService.getUserPolicies(user.id);
const driveCapacity = 1024 * 1024 * policies.driveCapacityMb;
+ const maxFileSize = 1024 * 1024 * policies.maxFileSizeMb;
this.registerLogger.debug('drive capacity override applied');
this.registerLogger.debug(`overrideCap: ${driveCapacity}bytes, usage: ${usage}bytes, u+s: ${usage + info.size}bytes`);
+ if (maxFileSize < info.size) {
+ if (isLocalUser) {
+ throw new IdentifiableError('f9e4e5f3-4df4-40b5-b400-f236945f7073', 'Max file size exceeded.');
+ }
+ }
+
// If usage limit exceeded
if (driveCapacity < usage + info.size) {
if (isLocalUser) {
diff --git a/packages/backend/src/core/FanoutTimelineEndpointService.ts b/packages/backend/src/core/FanoutTimelineEndpointService.ts
index 84ca06ec1e..af2723e99d 100644
--- a/packages/backend/src/core/FanoutTimelineEndpointService.ts
+++ b/packages/backend/src/core/FanoutTimelineEndpointService.ts
@@ -8,10 +8,12 @@ import { DI } from '@/di-symbols.js';
import { bindThis } from '@/decorators.js';
import type { MiUser } from '@/models/User.js';
import type { MiNote } from '@/models/Note.js';
+import type { MiMeta } from '@/models/Meta.js';
import { Packed } from '@/misc/json-schema.js';
import type { NotesRepository } from '@/models/_.js';
import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
import { FanoutTimelineName, FanoutTimelineService } from '@/core/FanoutTimelineService.js';
+import { UtilityService } from '@/core/UtilityService.js';
import { isUserRelated } from '@/misc/is-user-related.js';
import { isQuote, isRenote } from '@/misc/is-renote.js';
import { CacheService } from '@/core/CacheService.js';
@@ -30,6 +32,7 @@ type TimelineOptions = {
alwaysIncludeMyNotes?: boolean;
ignoreAuthorFromBlock?: boolean;
ignoreAuthorFromMute?: boolean;
+ ignoreAuthorFromInstanceBlock?: boolean;
excludeNoFiles?: boolean;
excludeReplies?: boolean;
excludeBots?: boolean;
@@ -43,9 +46,13 @@ export class FanoutTimelineEndpointService {
@Inject(DI.notesRepository)
private notesRepository: NotesRepository,
+ @Inject(DI.meta)
+ private meta: MiMeta,
+
private noteEntityService: NoteEntityService,
private cacheService: CacheService,
private fanoutTimelineService: FanoutTimelineService,
+ private utilityService: UtilityService,
) {
}
@@ -125,6 +132,19 @@ export class FanoutTimelineEndpointService {
};
}
+ {
+ const parentFilter = filter;
+ filter = (note) => {
+ if (!ps.ignoreAuthorFromInstanceBlock) {
+ if (this.utilityService.isBlockedHost(this.meta.blockedHosts, note.userHost)) return false;
+ }
+ if (note.userId !== note.renoteUserId && this.utilityService.isBlockedHost(this.meta.blockedHosts, note.renoteUserHost)) return false;
+ if (note.userId !== note.replyUserId && this.utilityService.isBlockedHost(this.meta.blockedHosts, note.replyUserHost)) return false;
+
+ return parentFilter(note);
+ };
+ }
+
const redisTimeline: MiNote[] = [];
let readFromRedis = 0;
let lastSuccessfulRate = 1; // rateをキャッシュする?
diff --git a/packages/backend/src/core/ImageProcessingService.ts b/packages/backend/src/core/ImageProcessingService.ts
index 6f978b34c8..6f60475442 100644
--- a/packages/backend/src/core/ImageProcessingService.ts
+++ b/packages/backend/src/core/ImageProcessingService.ts
@@ -34,6 +34,7 @@ export const webpDefault: sharp.WebpOptions = {
smartSubsample: true,
mixed: true,
effort: 2,
+ loop: 0,
};
export const avifDefault: sharp.AvifOptions = {
diff --git a/packages/backend/src/core/QueryService.ts b/packages/backend/src/core/QueryService.ts
index c611fe5f1e..fb4335b55d 100644
--- a/packages/backend/src/core/QueryService.ts
+++ b/packages/backend/src/core/QueryService.ts
@@ -7,7 +7,7 @@ import { Inject, Injectable } from '@nestjs/common';
import { Brackets, ObjectLiteral } from 'typeorm';
import { DI } from '@/di-symbols.js';
import type { MiUser } from '@/models/User.js';
-import type { UserProfilesRepository, FollowingsRepository, ChannelFollowingsRepository, BlockingsRepository, NoteThreadMutingsRepository, MutingsRepository, RenoteMutingsRepository } from '@/models/_.js';
+import type { UserProfilesRepository, FollowingsRepository, ChannelFollowingsRepository, BlockingsRepository, NoteThreadMutingsRepository, MutingsRepository, RenoteMutingsRepository, MiMeta } from '@/models/_.js';
import { bindThis } from '@/decorators.js';
import { IdService } from '@/core/IdService.js';
import type { SelectQueryBuilder } from 'typeorm';
@@ -36,6 +36,9 @@ export class QueryService {
@Inject(DI.renoteMutingsRepository)
private renoteMutingsRepository: RenoteMutingsRepository,
+ @Inject(DI.meta)
+ private meta: MiMeta,
+
private idService: IdService,
) {
}
@@ -251,4 +254,37 @@ export class QueryService {
q.setParameters(mutingQuery.getParameters());
}
+
+ @bindThis
+ public generateBlockedHostQueryForNote(q: SelectQueryBuilder<any>, excludeAuthor?: boolean): void {
+ let nonBlockedHostQuery: (part: string) => string;
+ if (this.meta.blockedHosts.length === 0) {
+ nonBlockedHostQuery = () => '1=1';
+ } else {
+ nonBlockedHostQuery = (match: string) => `${match} NOT ILIKE ALL(ARRAY[:...blocked])`;
+ q.setParameters({ blocked: this.meta.blockedHosts.flatMap(x => [x, `%.${x}`]) });
+ }
+
+ if (excludeAuthor) {
+ const instanceSuspension = (user: string) => new Brackets(qb => qb
+ .where(`note.${user}Id IS NULL`) // no corresponding user
+ .orWhere(`note.userId = note.${user}Id`)
+ .orWhere(`note.${user}Host IS NULL`) // local
+ .orWhere(nonBlockedHostQuery(`note.${user}Host`)));
+
+ q
+ .andWhere(instanceSuspension('replyUser'))
+ .andWhere(instanceSuspension('renoteUser'));
+ } else {
+ const instanceSuspension = (user: string) => new Brackets(qb => qb
+ .where(`note.${user}Id IS NULL`) // no corresponding user
+ .orWhere(`note.${user}Host IS NULL`) // local
+ .orWhere(nonBlockedHostQuery(`note.${user}Host`)));
+
+ q
+ .andWhere(instanceSuspension('user'))
+ .andWhere(instanceSuspension('replyUser'))
+ .andWhere(instanceSuspension('renoteUser'));
+ }
+ }
}
diff --git a/packages/backend/src/core/RoleService.ts b/packages/backend/src/core/RoleService.ts
index 8b98680f4c..229781c079 100644
--- a/packages/backend/src/core/RoleService.ts
+++ b/packages/backend/src/core/RoleService.ts
@@ -48,6 +48,7 @@ export type RolePolicies = {
canUseTranslator: boolean;
canHideAds: boolean;
driveCapacityMb: number;
+ maxFileSizeMb: number;
alwaysMarkNsfw: boolean;
canUpdateBioMedia: boolean;
pinLimit: number;
@@ -86,6 +87,7 @@ export const DEFAULT_POLICIES: RolePolicies = {
canUseTranslator: true,
canHideAds: false,
driveCapacityMb: 100,
+ maxFileSizeMb: 10,
alwaysMarkNsfw: false,
canUpdateBioMedia: true,
pinLimit: 5,
@@ -399,6 +401,7 @@ export class RoleService implements OnApplicationShutdown, OnModuleInit {
canUseTranslator: calc('canUseTranslator', vs => vs.some(v => v === true)),
canHideAds: calc('canHideAds', vs => vs.some(v => v === true)),
driveCapacityMb: calc('driveCapacityMb', vs => Math.max(...vs)),
+ maxFileSizeMb: calc('maxFileSizeMb', vs => Math.max(...vs)),
alwaysMarkNsfw: calc('alwaysMarkNsfw', vs => vs.some(v => v === true)),
canUpdateBioMedia: calc('canUpdateBioMedia', vs => vs.some(v => v === true)),
pinLimit: calc('pinLimit', vs => Math.max(...vs)),
diff --git a/packages/backend/src/core/SearchService.ts b/packages/backend/src/core/SearchService.ts
index e17677bcb5..37238dc4b0 100644
--- a/packages/backend/src/core/SearchService.ts
+++ b/packages/backend/src/core/SearchService.ts
@@ -300,6 +300,7 @@ export class SearchService {
}
this.queryService.generateVisibilityQuery(query, me);
+ this.queryService.generateBlockedHostQueryForNote(query);
if (me) this.queryService.generateMutedUserQueryForNotes(query, me);
if (me) this.queryService.generateBlockedUserQueryForNotes(query, me);
@@ -366,9 +367,14 @@ export class SearchService {
this.cacheService.userBlockedCache.fetch(me.id),
])
: [new Set<string>(), new Set<string>()];
- const notes = (await this.notesRepository.findBy({
- id: In(res.hits.map(x => x.id)),
- })).filter(note => {
+
+ const query = this.notesRepository.createQueryBuilder('note');
+
+ query.where('note.id IN (:...noteIds)', { noteIds: res.hits.map(x => x.id) });
+
+ this.queryService.generateBlockedHostQueryForNote(query);
+
+ const notes = (await query.getMany()).filter(note => {
if (me && isUserRelated(note, userIdsWhoBlockingMe)) return false;
if (me && isUserRelated(note, userIdsWhoMeMuting)) return false;
return true;