mirror of
https://github.com/misskey-dev/misskey.git
synced 2026-06-11 06:34:05 +02:00
Merge branch 'develop' into renovate/major-backend-update-dependencies
This commit is contained in:
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
export class AddUrlPreviewAllowRedirect1748310233000 {
|
||||
name = 'AddUrlPreviewAllowRedirect1748310233000'
|
||||
|
||||
async up(queryRunner) {
|
||||
await queryRunner.query(`ALTER TABLE "meta" ADD "urlPreviewAllowRedirect" boolean NOT NULL DEFAULT true`);
|
||||
}
|
||||
|
||||
async down(queryRunner) {
|
||||
await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "urlPreviewAllowRedirect"`);
|
||||
}
|
||||
}
|
||||
@@ -528,7 +528,7 @@ export class DriveService {
|
||||
return info.type.mime === mimeType;
|
||||
});
|
||||
if (!isAllowed) {
|
||||
throw new IdentifiableError('bd71c601-f9b0-4808-9137-a330647ced9b', 'Unallowed file type.');
|
||||
throw new IdentifiableError('bd71c601-f9b0-4808-9137-a330647ced9b', `Unallowed file type: ${info.type.mime}`);
|
||||
}
|
||||
|
||||
const driveCapacity = 1024 * 1024 * policies.driveCapacityMb;
|
||||
|
||||
@@ -120,6 +120,8 @@ export class FanoutTimelineEndpointService {
|
||||
filter = (note) => {
|
||||
if (isUserRelated(note, userIdsWhoBlockingMe, ps.ignoreAuthorFromBlock)) return false;
|
||||
if (isUserRelated(note, userIdsWhoMeMuting, ps.ignoreAuthorFromMute)) return false;
|
||||
if (isUserRelated(note.renote, userIdsWhoBlockingMe, ps.ignoreAuthorFromBlock)) return false;
|
||||
if (isUserRelated(note.renote, userIdsWhoMeMuting, ps.ignoreAuthorFromMute)) return false;
|
||||
if (!ps.ignoreAuthorFromMute && isRenote(note) && !isQuote(note) && userIdsWhoMeMutingRenotes.has(note.userId)) return false;
|
||||
if (isInstanceMuted(note, userMutedInstances)) return false;
|
||||
|
||||
|
||||
@@ -77,9 +77,51 @@ export class QueryService {
|
||||
return q;
|
||||
}
|
||||
|
||||
/**
|
||||
* ミュートやブロックのようにすべてのタイムラインで共通に使用するフィルターを定義します。
|
||||
*
|
||||
* 特別な事情がない限り、各タイムラインはこの関数を呼び出してフィルターを適用してください。
|
||||
*
|
||||
* Notes for future maintainers:
|
||||
* 1) この関数で生成するクエリと同等の処理が FanoutTimelineEndpointService にあります。
|
||||
* この関数を変更した場合、FanoutTimelineEndpointService の方も変更する必要があります。
|
||||
* 2) 以下のエンドポイントでは特別な事情があるため queryService のそれぞれの関数を呼び出しています。
|
||||
* この関数を変更した場合、以下のエンドポイントの方も変更する必要があることがあります。
|
||||
* - packages/backend/src/server/api/endpoints/clips/notes.ts
|
||||
*/
|
||||
@bindThis
|
||||
public generateBaseNoteFilteringQuery(
|
||||
query: SelectQueryBuilder<any>,
|
||||
me: { id: MiUser['id'] } | null,
|
||||
{
|
||||
excludeUserFromMute,
|
||||
excludeAuthor,
|
||||
}: {
|
||||
excludeUserFromMute?: MiUser['id'],
|
||||
excludeAuthor?: boolean,
|
||||
} = {},
|
||||
): void {
|
||||
this.generateBlockedHostQueryForNote(query, excludeAuthor);
|
||||
this.generateSuspendedUserQueryForNote(query, excludeAuthor);
|
||||
if (me) {
|
||||
this.generateMutedUserQueryForNotes(query, me, { excludeUserFromMute });
|
||||
this.generateBlockedUserQueryForNotes(query, me);
|
||||
this.generateMutedUserQueryForNotes(query, me, { noteColumn: 'renote', excludeUserFromMute });
|
||||
this.generateBlockedUserQueryForNotes(query, me, { noteColumn: 'renote' });
|
||||
}
|
||||
}
|
||||
|
||||
// ここでいうBlockedは被Blockedの意
|
||||
@bindThis
|
||||
public generateBlockedUserQueryForNotes(q: SelectQueryBuilder<any>, me: { id: MiUser['id'] }): void {
|
||||
public generateBlockedUserQueryForNotes(
|
||||
q: SelectQueryBuilder<any>,
|
||||
me: { id: MiUser['id'] },
|
||||
{
|
||||
noteColumn = 'note',
|
||||
}: {
|
||||
noteColumn?: string,
|
||||
} = {},
|
||||
): void {
|
||||
const blockingQuery = this.blockingsRepository.createQueryBuilder('blocking')
|
||||
.select('blocking.blockerId')
|
||||
.where('blocking.blockeeId = :blockeeId', { blockeeId: me.id });
|
||||
@@ -88,16 +130,20 @@ export class QueryService {
|
||||
// 投稿の返信先の作者にブロックされていない かつ
|
||||
// 投稿の引用元の作者にブロックされていない
|
||||
q
|
||||
.andWhere(`note.userId NOT IN (${ blockingQuery.getQuery() })`)
|
||||
.andWhere(new Brackets(qb => {
|
||||
qb
|
||||
.where('note.replyUserId IS NULL')
|
||||
.orWhere(`note.replyUserId NOT IN (${ blockingQuery.getQuery() })`);
|
||||
.where(`${noteColumn}.userId IS NULL`)
|
||||
.orWhere(`${noteColumn}.userId NOT IN (${ blockingQuery.getQuery() })`);
|
||||
}))
|
||||
.andWhere(new Brackets(qb => {
|
||||
qb
|
||||
.where('note.renoteUserId IS NULL')
|
||||
.orWhere(`note.renoteUserId NOT IN (${ blockingQuery.getQuery() })`);
|
||||
.where(`${noteColumn}.replyUserId IS NULL`)
|
||||
.orWhere(`${noteColumn}.replyUserId NOT IN (${ blockingQuery.getQuery() })`);
|
||||
}))
|
||||
.andWhere(new Brackets(qb => {
|
||||
qb
|
||||
.where(`${noteColumn}.renoteUserId IS NULL`)
|
||||
.orWhere(`${noteColumn}.renoteUserId NOT IN (${ blockingQuery.getQuery() })`);
|
||||
}));
|
||||
|
||||
q.setParameters(blockingQuery.getParameters());
|
||||
@@ -137,13 +183,23 @@ export class QueryService {
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public generateMutedUserQueryForNotes(q: SelectQueryBuilder<any>, me: { id: MiUser['id'] }, exclude?: { id: MiUser['id'] }): void {
|
||||
public generateMutedUserQueryForNotes(
|
||||
q: SelectQueryBuilder<any>,
|
||||
me: { id: MiUser['id'] },
|
||||
{
|
||||
excludeUserFromMute,
|
||||
noteColumn = 'note',
|
||||
}: {
|
||||
excludeUserFromMute?: MiUser['id'],
|
||||
noteColumn?: string,
|
||||
} = {},
|
||||
): void {
|
||||
const mutingQuery = this.mutingsRepository.createQueryBuilder('muting')
|
||||
.select('muting.muteeId')
|
||||
.where('muting.muterId = :muterId', { muterId: me.id });
|
||||
|
||||
if (exclude) {
|
||||
mutingQuery.andWhere('muting.muteeId != :excludeId', { excludeId: exclude.id });
|
||||
if (excludeUserFromMute) {
|
||||
mutingQuery.andWhere('muting.muteeId != :excludeId', { excludeId: excludeUserFromMute });
|
||||
}
|
||||
|
||||
const mutingInstanceQuery = this.userProfilesRepository.createQueryBuilder('user_profile')
|
||||
@@ -154,32 +210,36 @@ export class QueryService {
|
||||
// 投稿の返信先の作者をミュートしていない かつ
|
||||
// 投稿の引用元の作者をミュートしていない
|
||||
q
|
||||
.andWhere(`note.userId NOT IN (${ mutingQuery.getQuery() })`)
|
||||
.andWhere(new Brackets(qb => {
|
||||
qb
|
||||
.where('note.replyUserId IS NULL')
|
||||
.orWhere(`note.replyUserId NOT IN (${ mutingQuery.getQuery() })`);
|
||||
.where(`${noteColumn}.userId IS NULL`)
|
||||
.orWhere(`${noteColumn}.userId NOT IN (${ mutingQuery.getQuery() })`);
|
||||
}))
|
||||
.andWhere(new Brackets(qb => {
|
||||
qb
|
||||
.where('note.renoteUserId IS NULL')
|
||||
.orWhere(`note.renoteUserId NOT IN (${ mutingQuery.getQuery() })`);
|
||||
.where(`${noteColumn}.replyUserId IS NULL`)
|
||||
.orWhere(`${noteColumn}.replyUserId NOT IN (${ mutingQuery.getQuery() })`);
|
||||
}))
|
||||
.andWhere(new Brackets(qb => {
|
||||
qb
|
||||
.where(`${noteColumn}.renoteUserId IS NULL`)
|
||||
.orWhere(`${noteColumn}.renoteUserId NOT IN (${ mutingQuery.getQuery() })`);
|
||||
}))
|
||||
// mute instances
|
||||
.andWhere(new Brackets(qb => {
|
||||
qb
|
||||
.andWhere('note.userHost IS NULL')
|
||||
.orWhere(`NOT ((${ mutingInstanceQuery.getQuery() })::jsonb ? note.userHost)`);
|
||||
.andWhere(`${noteColumn}.userHost IS NULL`)
|
||||
.orWhere(`NOT ((${ mutingInstanceQuery.getQuery() })::jsonb ? ${noteColumn}.userHost)`);
|
||||
}))
|
||||
.andWhere(new Brackets(qb => {
|
||||
qb
|
||||
.where('note.replyUserHost IS NULL')
|
||||
.orWhere(`NOT ((${ mutingInstanceQuery.getQuery() })::jsonb ? note.replyUserHost)`);
|
||||
.where(`${noteColumn}.replyUserHost IS NULL`)
|
||||
.orWhere(`NOT ((${ mutingInstanceQuery.getQuery() })::jsonb ? ${noteColumn}.replyUserHost)`);
|
||||
}))
|
||||
.andWhere(new Brackets(qb => {
|
||||
qb
|
||||
.where('note.renoteUserHost IS NULL')
|
||||
.orWhere(`NOT ((${ mutingInstanceQuery.getQuery() })::jsonb ? note.renoteUserHost)`);
|
||||
.where(`${noteColumn}.renoteUserHost IS NULL`)
|
||||
.orWhere(`NOT ((${ mutingInstanceQuery.getQuery() })::jsonb ? ${noteColumn}.renoteUserHost)`);
|
||||
}));
|
||||
|
||||
q.setParameters(mutingQuery.getParameters());
|
||||
|
||||
@@ -234,10 +234,7 @@ export class SearchService {
|
||||
}
|
||||
|
||||
this.queryService.generateVisibilityQuery(query, me);
|
||||
this.queryService.generateBlockedHostQueryForNote(query);
|
||||
this.queryService.generateSuspendedUserQueryForNote(query);
|
||||
if (me) this.queryService.generateMutedUserQueryForNotes(query, me);
|
||||
if (me) this.queryService.generateBlockedUserQueryForNotes(query, me);
|
||||
this.queryService.generateBaseNoteFilteringQuery(query, me);
|
||||
|
||||
return query.limit(pagination.limit).getMany();
|
||||
}
|
||||
|
||||
@@ -91,7 +91,7 @@ export class UserListService implements OnApplicationShutdown, OnModuleInit {
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public async addMember(target: MiUser, list: MiUserList, me: MiUser) {
|
||||
public async addMember(target: MiUser, list: MiUserList, me: MiUser, options: { withReplies?: boolean } = {}) {
|
||||
const currentCount = await this.userListMembershipsRepository.countBy({
|
||||
userListId: list.id,
|
||||
});
|
||||
@@ -104,6 +104,7 @@ export class UserListService implements OnApplicationShutdown, OnModuleInit {
|
||||
userId: target.id,
|
||||
userListId: list.id,
|
||||
userListUserId: list.userId,
|
||||
withReplies: options.withReplies ?? false,
|
||||
} as MiUserListMembership);
|
||||
|
||||
this.globalEventService.publishInternalEvent('userListMemberAdded', { userListId: list.id, memberId: target.id });
|
||||
|
||||
@@ -3,7 +3,17 @@
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
export function isUserRelated(note: any, userIds: Set<string>, ignoreAuthor = false): boolean {
|
||||
import type { MiUser } from '@/models/_.js';
|
||||
|
||||
interface NoteLike {
|
||||
userId: MiUser['id'];
|
||||
reply?: NoteLike | null;
|
||||
renote?: NoteLike | null;
|
||||
replyUserId?: MiUser['id'] | null;
|
||||
renoteUserId?: MiUser['id'] | null;
|
||||
}
|
||||
|
||||
export function isUserRelated(note: NoteLike | null | undefined, userIds: Set<string>, ignoreAuthor = false): boolean {
|
||||
if (!note) {
|
||||
return false;
|
||||
}
|
||||
@@ -12,13 +22,16 @@ export function isUserRelated(note: any, userIds: Set<string>, ignoreAuthor = fa
|
||||
return true;
|
||||
}
|
||||
|
||||
if (note.reply != null && note.reply.userId !== note.userId && userIds.has(note.reply.userId)) {
|
||||
const replyUserId = note.replyUserId ?? note.reply?.userId;
|
||||
if (replyUserId != null && replyUserId !== note.userId && userIds.has(replyUserId)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (note.renote != null && note.renote.userId !== note.userId && userIds.has(note.renote.userId)) {
|
||||
const renoteUserId = note.renoteUserId ?? note.renote?.userId;
|
||||
if (renoteUserId != null && renoteUserId !== note.userId && userIds.has(renoteUserId)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -619,6 +619,11 @@ export class MiMeta {
|
||||
})
|
||||
public urlPreviewEnabled: boolean;
|
||||
|
||||
@Column('boolean', {
|
||||
default: true,
|
||||
})
|
||||
public urlPreviewAllowRedirect: boolean;
|
||||
|
||||
@Column('integer', {
|
||||
default: 10000,
|
||||
})
|
||||
|
||||
@@ -94,7 +94,8 @@ export class ExportFollowingProcessorService {
|
||||
continue;
|
||||
}
|
||||
|
||||
const content = this.utilityService.getFullApAccount(u.username, u.host);
|
||||
const userAcct = this.utilityService.getFullApAccount(u.username, u.host);
|
||||
const content = `${userAcct},withReplies=${following.withReplies}`;
|
||||
await new Promise<void>((res, rej) => {
|
||||
stream.write(content + '\n', err => {
|
||||
if (err) {
|
||||
|
||||
@@ -67,10 +67,12 @@ export class ExportUserListsProcessorService {
|
||||
const users = await this.usersRepository.findBy({
|
||||
id: In(memberships.map(j => j.userId)),
|
||||
});
|
||||
const usersWithReplies = new Set(memberships.filter(m => m.withReplies).map(m => m.userId));
|
||||
|
||||
for (const u of users) {
|
||||
const acct = this.utilityService.getFullApAccount(u.username, u.host);
|
||||
const content = `${list.name},${acct}`;
|
||||
// 3rd column and later will be key=value pairs
|
||||
const content = `${list.name},${acct},withReplies=${usersWithReplies.has(u.id)}`;
|
||||
await new Promise<void>((res, rej) => {
|
||||
stream.write(content + '\n', err => {
|
||||
if (err) {
|
||||
|
||||
@@ -67,8 +67,19 @@ export class ImportFollowingProcessorService {
|
||||
const user = job.data.user;
|
||||
|
||||
try {
|
||||
const acct = line.split(',')[0].trim();
|
||||
const parts = line.split(',');
|
||||
const acct = parts[0].trim();
|
||||
const { username, host } = Acct.parse(acct);
|
||||
let withReplies: boolean | null = null;
|
||||
|
||||
for (const keyValue of parts.slice(2)) {
|
||||
const [key, value] = keyValue.split('=');
|
||||
switch (key) {
|
||||
case 'withReplies':
|
||||
withReplies = value === 'true';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!host) return;
|
||||
|
||||
@@ -95,7 +106,7 @@ export class ImportFollowingProcessorService {
|
||||
|
||||
this.logger.info(`Follow ${target.id} ${job.data.withReplies ? 'with replies' : 'without replies'} ...`);
|
||||
|
||||
this.queueService.createFollowJob([{ from: user, to: { id: target.id }, silent: true, withReplies: job.data.withReplies }]);
|
||||
await this.queueService.createFollowJob([{ from: user, to: { id: target.id }, silent: true, withReplies: withReplies ?? job.data.withReplies }]);
|
||||
} catch (e) {
|
||||
this.logger.warn(`Error: ${e}`);
|
||||
}
|
||||
|
||||
@@ -70,8 +70,19 @@ export class ImportUserListsProcessorService {
|
||||
linenum++;
|
||||
|
||||
try {
|
||||
const listName = line.split(',')[0].trim();
|
||||
const { username, host } = Acct.parse(line.split(',')[1].trim());
|
||||
const parts = line.split(',');
|
||||
const listName = parts[0].trim();
|
||||
const { username, host } = Acct.parse(parts[1].trim());
|
||||
let withReplies = false;
|
||||
|
||||
for (const keyValue of parts.slice(2)) {
|
||||
const [key, value] = keyValue.split('=');
|
||||
switch (key) {
|
||||
case 'withReplies':
|
||||
withReplies = value === 'true';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let list = await this.userListsRepository.findOneBy({
|
||||
userId: user.id,
|
||||
@@ -100,7 +111,9 @@ export class ImportUserListsProcessorService {
|
||||
|
||||
if (await this.userListMembershipsRepository.findOneBy({ userListId: list!.id, userId: target.id }) != null) continue;
|
||||
|
||||
this.userListService.addMember(target, list!, user);
|
||||
await this.userListService.addMember(target, list, user, {
|
||||
withReplies: withReplies,
|
||||
});
|
||||
} catch (e) {
|
||||
this.logger.warn(`Error in line:${linenum} ${e}`);
|
||||
}
|
||||
|
||||
@@ -495,6 +495,10 @@ export const meta = {
|
||||
type: 'boolean',
|
||||
optional: false, nullable: false,
|
||||
},
|
||||
urlPreviewAllowRedirect: {
|
||||
type: 'boolean',
|
||||
optional: false, nullable: false,
|
||||
},
|
||||
urlPreviewTimeout: {
|
||||
type: 'number',
|
||||
optional: false, nullable: false,
|
||||
@@ -704,6 +708,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||
notesPerOneAd: instance.notesPerOneAd,
|
||||
summalyProxy: instance.urlPreviewSummaryProxyUrl,
|
||||
urlPreviewEnabled: instance.urlPreviewEnabled,
|
||||
urlPreviewAllowRedirect: instance.urlPreviewAllowRedirect,
|
||||
urlPreviewTimeout: instance.urlPreviewTimeout,
|
||||
urlPreviewMaximumContentLength: instance.urlPreviewMaximumContentLength,
|
||||
urlPreviewRequireContentLength: instance.urlPreviewRequireContentLength,
|
||||
|
||||
@@ -170,6 +170,7 @@ export const paramDef = {
|
||||
description: '[Deprecated] Use "urlPreviewSummaryProxyUrl" instead.',
|
||||
},
|
||||
urlPreviewEnabled: { type: 'boolean' },
|
||||
urlPreviewAllowRedirect: { type: 'boolean' },
|
||||
urlPreviewTimeout: { type: 'integer' },
|
||||
urlPreviewMaximumContentLength: { type: 'integer' },
|
||||
urlPreviewRequireContentLength: { type: 'boolean' },
|
||||
@@ -664,6 +665,10 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||
set.urlPreviewEnabled = ps.urlPreviewEnabled;
|
||||
}
|
||||
|
||||
if (ps.urlPreviewAllowRedirect !== undefined) {
|
||||
set.urlPreviewAllowRedirect = ps.urlPreviewAllowRedirect;
|
||||
}
|
||||
|
||||
if (ps.urlPreviewTimeout !== undefined) {
|
||||
set.urlPreviewTimeout = ps.urlPreviewTimeout;
|
||||
}
|
||||
|
||||
@@ -111,11 +111,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||
// NOTE: センシティブ除外の設定はこのエンドポイントでは無視する。
|
||||
// https://github.com/misskey-dev/misskey/pull/15346#discussion_r1929950255
|
||||
|
||||
this.queryService.generateBlockedHostQueryForNote(query);
|
||||
this.queryService.generateSuspendedUserQueryForNote(query);
|
||||
this.queryService.generateVisibilityQuery(query, me);
|
||||
this.queryService.generateMutedUserQueryForNotes(query, me);
|
||||
this.queryService.generateBlockedUserQueryForNotes(query, me);
|
||||
this.queryService.generateBaseNoteFilteringQuery(query, me);
|
||||
|
||||
const notes = await query.getMany();
|
||||
if (sinceId != null && untilId == null) {
|
||||
|
||||
@@ -121,12 +121,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||
.leftJoinAndSelect('renote.user', 'renoteUser')
|
||||
.leftJoinAndSelect('note.channel', 'channel');
|
||||
|
||||
this.queryService.generateBlockedHostQueryForNote(query);
|
||||
this.queryService.generateSuspendedUserQueryForNote(query);
|
||||
if (me) {
|
||||
this.queryService.generateMutedUserQueryForNotes(query, me);
|
||||
this.queryService.generateBlockedUserQueryForNotes(query, me);
|
||||
}
|
||||
this.queryService.generateBaseNoteFilteringQuery(query, me);
|
||||
//#endregion
|
||||
|
||||
return await query.limit(ps.limit).getMany();
|
||||
|
||||
@@ -91,6 +91,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||
if (me) {
|
||||
this.queryService.generateMutedUserQueryForNotes(query, me);
|
||||
this.queryService.generateBlockedUserQueryForNotes(query, me);
|
||||
this.queryService.generateMutedUserQueryForNotes(query, me, { noteColumn: 'renote' });
|
||||
this.queryService.generateBlockedUserQueryForNotes(query, me, { noteColumn: 'renote' });
|
||||
}
|
||||
|
||||
const notes = await query
|
||||
|
||||
@@ -70,12 +70,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||
.leftJoinAndSelect('renote.user', 'renoteUser');
|
||||
|
||||
this.queryService.generateVisibilityQuery(query, me);
|
||||
this.queryService.generateBlockedHostQueryForNote(query);
|
||||
this.queryService.generateSuspendedUserQueryForNote(query);
|
||||
if (me) {
|
||||
this.queryService.generateMutedUserQueryForNotes(query, me);
|
||||
this.queryService.generateBlockedUserQueryForNotes(query, me);
|
||||
}
|
||||
this.queryService.generateBaseNoteFilteringQuery(query, me);
|
||||
|
||||
const notes = await query.limit(ps.limit).getMany();
|
||||
|
||||
|
||||
@@ -78,11 +78,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||
.leftJoinAndSelect('reply.user', 'replyUser')
|
||||
.leftJoinAndSelect('renote.user', 'renoteUser');
|
||||
|
||||
if (me) {
|
||||
this.queryService.generateMutedUserQueryForNotes(query, me);
|
||||
this.queryService.generateBlockedUserQueryForNotes(query, me);
|
||||
this.queryService.generateMutedUserRenotesQueryForNotes(query, me);
|
||||
}
|
||||
this.queryService.generateBaseNoteFilteringQuery(query, me);
|
||||
if (me) this.queryService.generateMutedUserRenotesQueryForNotes(query, me);
|
||||
|
||||
if (ps.withFiles) {
|
||||
query.andWhere('note.fileIds != \'{}\'');
|
||||
|
||||
@@ -243,10 +243,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||
}
|
||||
|
||||
this.queryService.generateVisibilityQuery(query, me);
|
||||
this.queryService.generateBlockedHostQueryForNote(query);
|
||||
this.queryService.generateSuspendedUserQueryForNote(query);
|
||||
this.queryService.generateMutedUserQueryForNotes(query, me);
|
||||
this.queryService.generateBlockedUserQueryForNotes(query, me);
|
||||
this.queryService.generateBaseNoteFilteringQuery(query, me);
|
||||
this.queryService.generateMutedUserRenotesQueryForNotes(query, me);
|
||||
|
||||
if (ps.includeMyRenotes === false) {
|
||||
|
||||
@@ -156,10 +156,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||
.leftJoinAndSelect('renote.user', 'renoteUser');
|
||||
|
||||
this.queryService.generateVisibilityQuery(query, me);
|
||||
this.queryService.generateBlockedHostQueryForNote(query);
|
||||
this.queryService.generateSuspendedUserQueryForNote(query);
|
||||
if (me) this.queryService.generateMutedUserQueryForNotes(query, me);
|
||||
if (me) this.queryService.generateBlockedUserQueryForNotes(query, me);
|
||||
this.queryService.generateBaseNoteFilteringQuery(query, me);
|
||||
if (me) this.queryService.generateMutedUserRenotesQueryForNotes(query, me);
|
||||
|
||||
if (ps.withFiles) {
|
||||
|
||||
@@ -72,11 +72,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||
.leftJoinAndSelect('renote.user', 'renoteUser');
|
||||
|
||||
this.queryService.generateVisibilityQuery(query, me);
|
||||
this.queryService.generateBlockedHostQueryForNote(query);
|
||||
this.queryService.generateSuspendedUserQueryForNote(query);
|
||||
this.queryService.generateMutedUserQueryForNotes(query, me);
|
||||
this.queryService.generateBaseNoteFilteringQuery(query, me);
|
||||
this.queryService.generateMutedNoteThreadQuery(query, me);
|
||||
this.queryService.generateBlockedUserQueryForNotes(query, me);
|
||||
|
||||
if (ps.visibility) {
|
||||
query.andWhere('note.visibility = :visibility', { visibility: ps.visibility });
|
||||
|
||||
@@ -72,10 +72,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||
.leftJoinAndSelect('renote.user', 'renoteUser');
|
||||
|
||||
this.queryService.generateVisibilityQuery(query, me);
|
||||
this.queryService.generateBlockedHostQueryForNote(query);
|
||||
this.queryService.generateSuspendedUserQueryForNote(query);
|
||||
if (me) this.queryService.generateMutedUserQueryForNotes(query, me);
|
||||
if (me) this.queryService.generateBlockedUserQueryForNotes(query, me);
|
||||
this.queryService.generateBaseNoteFilteringQuery(query, me);
|
||||
|
||||
const renotes = await query.limit(ps.limit).getMany();
|
||||
|
||||
|
||||
@@ -56,10 +56,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||
.leftJoinAndSelect('renote.user', 'renoteUser');
|
||||
|
||||
this.queryService.generateVisibilityQuery(query, me);
|
||||
this.queryService.generateBlockedHostQueryForNote(query);
|
||||
this.queryService.generateSuspendedUserQueryForNote(query);
|
||||
if (me) this.queryService.generateMutedUserQueryForNotes(query, me);
|
||||
if (me) this.queryService.generateBlockedUserQueryForNotes(query, me);
|
||||
this.queryService.generateBaseNoteFilteringQuery(query, me);
|
||||
|
||||
const timeline = await query.limit(ps.limit).getMany();
|
||||
|
||||
|
||||
@@ -96,10 +96,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||
.leftJoinAndSelect('renote.user', 'renoteUser');
|
||||
|
||||
this.queryService.generateVisibilityQuery(query, me);
|
||||
this.queryService.generateBlockedHostQueryForNote(query);
|
||||
this.queryService.generateSuspendedUserQueryForNote(query);
|
||||
if (me) this.queryService.generateMutedUserQueryForNotes(query, me);
|
||||
if (me) this.queryService.generateBlockedUserQueryForNotes(query, me);
|
||||
this.queryService.generateBaseNoteFilteringQuery(query, me);
|
||||
|
||||
try {
|
||||
if ('tag' in ps) {
|
||||
|
||||
@@ -199,10 +199,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||
}));
|
||||
|
||||
this.queryService.generateVisibilityQuery(query, me);
|
||||
this.queryService.generateBlockedHostQueryForNote(query);
|
||||
this.queryService.generateSuspendedUserQueryForNote(query);
|
||||
this.queryService.generateMutedUserQueryForNotes(query, me);
|
||||
this.queryService.generateBlockedUserQueryForNotes(query, me);
|
||||
this.queryService.generateBaseNoteFilteringQuery(query, me);
|
||||
this.queryService.generateMutedUserRenotesQueryForNotes(query, me);
|
||||
|
||||
if (ps.includeMyRenotes === false) {
|
||||
|
||||
@@ -184,10 +184,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||
}));
|
||||
|
||||
this.queryService.generateVisibilityQuery(query, me);
|
||||
this.queryService.generateBlockedHostQueryForNote(query);
|
||||
this.queryService.generateSuspendedUserQueryForNote(query);
|
||||
this.queryService.generateMutedUserQueryForNotes(query, me);
|
||||
this.queryService.generateBlockedUserQueryForNotes(query, me);
|
||||
this.queryService.generateBaseNoteFilteringQuery(query, me);
|
||||
this.queryService.generateMutedUserRenotesQueryForNotes(query, me);
|
||||
|
||||
if (ps.includeMyRenotes === false) {
|
||||
|
||||
@@ -102,10 +102,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||
.leftJoinAndSelect('renote.user', 'renoteUser');
|
||||
|
||||
this.queryService.generateVisibilityQuery(query, me);
|
||||
this.queryService.generateBlockedHostQueryForNote(query);
|
||||
this.queryService.generateSuspendedUserQueryForNote(query);
|
||||
this.queryService.generateMutedUserQueryForNotes(query, me);
|
||||
this.queryService.generateBlockedUserQueryForNotes(query, me);
|
||||
this.queryService.generateBaseNoteFilteringQuery(query, me);
|
||||
|
||||
const notes = await query.getMany();
|
||||
notes.sort((a, b) => a.id > b.id ? -1 : 1);
|
||||
|
||||
@@ -186,12 +186,10 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||
}
|
||||
|
||||
this.queryService.generateVisibilityQuery(query, me);
|
||||
this.queryService.generateBlockedHostQueryForNote(query, true);
|
||||
this.queryService.generateSuspendedUserQueryForNote(query, true);
|
||||
if (me) {
|
||||
this.queryService.generateMutedUserQueryForNotes(query, me, { id: ps.userId });
|
||||
this.queryService.generateBlockedUserQueryForNotes(query, me);
|
||||
}
|
||||
this.queryService.generateBaseNoteFilteringQuery(query, me, {
|
||||
excludeAuthor: true,
|
||||
excludeUserFromMute: ps.userId,
|
||||
});
|
||||
|
||||
if (ps.withFiles) {
|
||||
query.andWhere('note.fileIds != \'{}\'');
|
||||
|
||||
@@ -64,6 +64,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||
this.queryService.generateMutedUserQueryForUsers(query, me);
|
||||
this.queryService.generateBlockQueryForUsers(query, me);
|
||||
this.queryService.generateBlockedUserQueryForNotes(query, me);
|
||||
this.queryService.generateBlockedUserQueryForNotes(query, me, { noteColumn: 'renote' });
|
||||
|
||||
const followingQuery = this.followingsRepository.createQueryBuilder('following')
|
||||
.select('following.followeeId')
|
||||
|
||||
@@ -122,7 +122,7 @@ export class UrlPreviewService {
|
||||
: undefined;
|
||||
|
||||
return summaly(url, {
|
||||
followRedirects: false,
|
||||
followRedirects: this.meta.urlPreviewAllowRedirect,
|
||||
lang: lang ?? 'ja-JP',
|
||||
agent: agent,
|
||||
userAgent: meta.urlPreviewUserAgent ?? undefined,
|
||||
@@ -137,6 +137,7 @@ export class UrlPreviewService {
|
||||
const queryStr = query({
|
||||
url: url,
|
||||
lang: lang ?? 'ja-JP',
|
||||
followRedirects: this.meta.urlPreviewAllowRedirect,
|
||||
userAgent: meta.urlPreviewUserAgent ?? undefined,
|
||||
operationTimeout: meta.urlPreviewTimeout,
|
||||
contentLengthLimit: meta.urlPreviewMaximumContentLength,
|
||||
|
||||
@@ -9,9 +9,9 @@ import * as assert from 'assert';
|
||||
// node-fetch only supports it's own Blob yet
|
||||
// https://github.com/node-fetch/node-fetch/pull/1664
|
||||
import { Blob } from 'node-fetch';
|
||||
import { MiUser } from '@/models/_.js';
|
||||
import { api, castAsError, initTestDb, post, signup, simpleGet, uploadFile } from '../utils.js';
|
||||
import type * as misskey from 'misskey-js';
|
||||
import { MiUser } from '@/models/_.js';
|
||||
|
||||
describe('Endpoints', () => {
|
||||
let alice: misskey.entities.SignupResponse;
|
||||
@@ -572,19 +572,10 @@ describe('Endpoints', () => {
|
||||
|
||||
describe('drive', () => {
|
||||
test('ドライブ情報を取得できる', async () => {
|
||||
await uploadFile(alice, {
|
||||
blob: new Blob([new Uint8Array(256)]),
|
||||
});
|
||||
await uploadFile(alice, {
|
||||
blob: new Blob([new Uint8Array(512)]),
|
||||
});
|
||||
await uploadFile(alice, {
|
||||
blob: new Blob([new Uint8Array(1024)]),
|
||||
});
|
||||
const res = await api('drive', {}, alice);
|
||||
assert.strictEqual(res.status, 200);
|
||||
assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true);
|
||||
expect(res.body).toHaveProperty('usage', 1792);
|
||||
expect(res.body).toHaveProperty('usage', 0);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -345,6 +345,44 @@ describe('Timelines', () => {
|
||||
assert.strictEqual(res.body.some(note => note.id === carolNote.id), false);
|
||||
});
|
||||
|
||||
test.concurrent('ミュートしているユーザーのノートの、関係のないユーザによる引用ノートの、フォローしているユーザーによるリノートが含まれない', async () => {
|
||||
const [alice, bob, carol, dave] = await Promise.all([signup(), signup(), signup(), signup()]);
|
||||
|
||||
await api('following/create', { userId: bob.id }, alice);
|
||||
await api('mute/create', { userId: carol.id }, alice);
|
||||
await setTimeout(1000);
|
||||
const carolNote = await post(carol, { text: 'hi' });
|
||||
const daveNote = await post(dave, { text: 'quote hi', renoteId: carolNote.id });
|
||||
const bobNote = await post(bob, { renoteId: daveNote.id });
|
||||
|
||||
await waitForPushToTl();
|
||||
|
||||
const res = await api('notes/timeline', { limit: 100 }, alice);
|
||||
|
||||
assert.strictEqual(res.body.some(note => note.id === carolNote.id), false);
|
||||
assert.strictEqual(res.body.some(note => note.id === daveNote.id), false);
|
||||
assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
|
||||
});
|
||||
|
||||
test.concurrent('ミュートしているユーザーのノートの、関係のないユーザによるリプライの、フォローしているユーザーによるリノートが含まれない', async () => {
|
||||
const [alice, bob, carol, dave] = await Promise.all([signup(), signup(), signup(), signup()]);
|
||||
|
||||
await api('following/create', { userId: bob.id }, alice);
|
||||
await api('mute/create', { userId: carol.id }, alice);
|
||||
await setTimeout(1000);
|
||||
const carolNote = await post(carol, { text: 'hi' });
|
||||
const daveNote = await post(dave, { text: 'quote hi', replyId: carolNote.id });
|
||||
const bobNote = await post(bob, { renoteId: daveNote.id });
|
||||
|
||||
await waitForPushToTl();
|
||||
|
||||
const res = await api('notes/timeline', { limit: 100 }, alice);
|
||||
|
||||
assert.strictEqual(res.body.some(note => note.id === carolNote.id), false);
|
||||
assert.strictEqual(res.body.some(note => note.id === daveNote.id), false);
|
||||
assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
|
||||
});
|
||||
|
||||
test.concurrent('フォローしているリモートユーザーのノートが含まれる', async () => {
|
||||
const [alice, bob] = await Promise.all([signup(), signup({ host: genHost() })]);
|
||||
|
||||
@@ -687,6 +725,42 @@ describe('Timelines', () => {
|
||||
assert.strictEqual(res.body.some(note => note.id === carolNote.id), false);
|
||||
});
|
||||
|
||||
test.concurrent('ミュートしているユーザーのノートの、関係のないユーザによる引用ノートの、リノートが含まれない', async () => {
|
||||
const [alice, bob, carol, dave] = await Promise.all([signup(), signup(), signup(), signup()]);
|
||||
|
||||
await api('mute/create', { userId: carol.id }, alice);
|
||||
await setTimeout(1000);
|
||||
const carolNote = await post(carol, { text: 'hi' });
|
||||
const daveNote = await post(dave, { text: 'quote hi', renoteId: carolNote.id });
|
||||
const bobNote = await post(bob, { renoteId: daveNote.id });
|
||||
|
||||
await waitForPushToTl();
|
||||
|
||||
const res = await api('notes/local-timeline', { limit: 100 }, alice);
|
||||
|
||||
assert.strictEqual(res.body.some(note => note.id === carolNote.id), false);
|
||||
assert.strictEqual(res.body.some(note => note.id === daveNote.id), false);
|
||||
assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
|
||||
});
|
||||
|
||||
test.concurrent('ミュートしているユーザーのノートの、関係のないユーザによるリプライの、リノートが含まれない', async () => {
|
||||
const [alice, bob, carol, dave] = await Promise.all([signup(), signup(), signup(), signup()]);
|
||||
|
||||
await api('mute/create', { userId: carol.id }, alice);
|
||||
await setTimeout(1000);
|
||||
const carolNote = await post(carol, { text: 'hi' });
|
||||
const daveNote = await post(dave, { text: 'quote hi', replyId: carolNote.id });
|
||||
const bobNote = await post(bob, { renoteId: daveNote.id });
|
||||
|
||||
await waitForPushToTl();
|
||||
|
||||
const res = await api('notes/local-timeline', { limit: 100 }, alice);
|
||||
|
||||
assert.strictEqual(res.body.some(note => note.id === carolNote.id), false);
|
||||
assert.strictEqual(res.body.some(note => note.id === daveNote.id), false);
|
||||
assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
|
||||
});
|
||||
|
||||
test.concurrent('withReplies: false でフォローしているユーザーからの自分への返信が含まれる', async () => {
|
||||
const [alice, bob] = await Promise.all([signup(), signup()]);
|
||||
|
||||
@@ -1383,6 +1457,39 @@ describe('Timelines', () => {
|
||||
assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
|
||||
});
|
||||
|
||||
test.concurrent('ミュートしているユーザーのノートの、関係のないユーザによる引用ノートの、リノートが含まれない', async () => {
|
||||
const [alice, bob, carol, dave] = await Promise.all([signup(), signup(), signup(), signup()]);
|
||||
|
||||
await api('mute/create', { userId: carol.id }, alice);
|
||||
await setTimeout(1000);
|
||||
const carolNote = await post(carol, { text: 'hi' });
|
||||
const daveNote = await post(dave, { text: 'quote hi', renoteId: carolNote.id });
|
||||
const bobNote = await post(bob, { renoteId: daveNote.id });
|
||||
|
||||
await waitForPushToTl();
|
||||
|
||||
const res = await api('users/notes', { userId: bob.id, limit: 100 }, alice);
|
||||
|
||||
assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
|
||||
});
|
||||
|
||||
test.concurrent('ミュートしているユーザーのノートの、関係のないユーザによるリプライの、リノートが含まれない', async () => {
|
||||
const [alice, bob, carol, dave] = await Promise.all([signup(), signup(), signup(), signup()]);
|
||||
|
||||
await api('following/create', { userId: bob.id }, alice);
|
||||
await api('mute/create', { userId: carol.id }, alice);
|
||||
await setTimeout(1000);
|
||||
const carolNote = await post(carol, { text: 'hi' });
|
||||
const daveNote = await post(dave, { text: 'quote hi', replyId: carolNote.id });
|
||||
const bobNote = await post(bob, { renoteId: daveNote.id });
|
||||
|
||||
await waitForPushToTl();
|
||||
|
||||
const res = await api('users/notes', { userId: bob.id, limit: 100 }, alice);
|
||||
|
||||
assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
|
||||
});
|
||||
|
||||
test.concurrent('ミュートしていても userId に指定したユーザーの投稿が含まれる', async () => {
|
||||
const [alice, bob] = await Promise.all([signup(), signup()]);
|
||||
|
||||
@@ -1391,6 +1498,8 @@ describe('Timelines', () => {
|
||||
const bobNote1 = await post(bob, { text: 'hi' });
|
||||
const bobNote2 = await post(bob, { text: 'hi', replyId: bobNote1.id });
|
||||
const bobNote3 = await post(bob, { text: 'hi', renoteId: bobNote1.id });
|
||||
const bobNote4 = await post(bob, { renoteId: bobNote2.id });
|
||||
const bobNote5 = await post(bob, { renoteId: bobNote3.id });
|
||||
|
||||
await waitForPushToTl();
|
||||
|
||||
@@ -1399,6 +1508,8 @@ describe('Timelines', () => {
|
||||
assert.strictEqual(res.body.some(note => note.id === bobNote1.id), true);
|
||||
assert.strictEqual(res.body.some(note => note.id === bobNote2.id), true);
|
||||
assert.strictEqual(res.body.some(note => note.id === bobNote3.id), true);
|
||||
assert.strictEqual(res.body.some(note => note.id === bobNote4.id), true);
|
||||
assert.strictEqual(res.body.some(note => note.id === bobNote5.id), true);
|
||||
});
|
||||
|
||||
test.concurrent('自身の visibility: specified なノートが含まれる', async () => {
|
||||
|
||||
BIN
packages/backend/test/resources/hw.png
Normal file
BIN
packages/backend/test/resources/hw.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 96 KiB |
@@ -6,9 +6,15 @@
|
||||
process.env.NODE_ENV = 'test';
|
||||
|
||||
import * as assert from 'assert';
|
||||
import * as fs from 'node:fs';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { dirname } from 'node:path';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import { jest } from '@jest/globals';
|
||||
|
||||
import { MockResolver } from '../misc/mock-resolver.js';
|
||||
import type { IActor, IApDocument, ICollection, IObject, IPost } from '@/core/activitypub/type.js';
|
||||
import type { MiRemoteUser } from '@/models/User.js';
|
||||
import { ApImageService } from '@/core/activitypub/models/ApImageService.js';
|
||||
import { ApNoteService } from '@/core/activitypub/models/ApNoteService.js';
|
||||
import { ApPersonService } from '@/core/activitypub/models/ApPersonService.js';
|
||||
@@ -19,14 +25,14 @@ import { GlobalModule } from '@/GlobalModule.js';
|
||||
import { CoreModule } from '@/core/CoreModule.js';
|
||||
import { FederatedInstanceService } from '@/core/FederatedInstanceService.js';
|
||||
import { LoggerService } from '@/core/LoggerService.js';
|
||||
import type { IActor, IApDocument, ICollection, IObject, IPost } from '@/core/activitypub/type.js';
|
||||
import { MiMeta, MiNote, UserProfilesRepository } from '@/models/_.js';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import { secureRndstr } from '@/misc/secure-rndstr.js';
|
||||
import { DownloadService } from '@/core/DownloadService.js';
|
||||
import type { MiRemoteUser } from '@/models/User.js';
|
||||
import { genAidx } from '@/misc/id/aidx.js';
|
||||
import { MockResolver } from '../misc/mock-resolver.js';
|
||||
|
||||
const _filename = fileURLToPath(import.meta.url);
|
||||
const _dirname = dirname(_filename);
|
||||
|
||||
const host = 'https://host1.test';
|
||||
|
||||
@@ -120,7 +126,13 @@ describe('ActivityPub', () => {
|
||||
imports: [GlobalModule, CoreModule],
|
||||
})
|
||||
.overrideProvider(DownloadService).useValue({
|
||||
async downloadUrl(): Promise<{ filename: string }> {
|
||||
async downloadUrl(url: string, path: string): Promise<{ filename: string }> {
|
||||
if (url.endsWith('.png')) {
|
||||
fs.copyFileSync(
|
||||
_dirname + '/../resources/hw.png',
|
||||
path,
|
||||
);
|
||||
}
|
||||
return {
|
||||
filename: 'dummy.tmp',
|
||||
};
|
||||
@@ -440,7 +452,7 @@ describe('ActivityPub', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('JSON-LD', () =>{
|
||||
describe('JSON-LD', () => {
|
||||
test('Compaction', async () => {
|
||||
const jsonLd = jsonLdService.use();
|
||||
|
||||
|
||||
@@ -17,6 +17,8 @@ import { ServerModule } from '@/server/ServerModule.js';
|
||||
import { ServerService } from '@/server/ServerService.js';
|
||||
import { IdService } from '@/core/IdService.js';
|
||||
|
||||
// TODO: uploadableFileTypes で許可されていないファイルが弾かれるかのテスト
|
||||
|
||||
describe('/drive/files/create', () => {
|
||||
let module: TestingModule;
|
||||
let server: FastifyInstance;
|
||||
@@ -25,6 +27,8 @@ describe('/drive/files/create', () => {
|
||||
|
||||
let root: MiUser;
|
||||
let role_tinyAttachment: MiRole;
|
||||
let role_imageOnly: MiRole;
|
||||
let role_allowAllTypes: MiRole;
|
||||
|
||||
let folder: MiDriveFolder;
|
||||
|
||||
@@ -64,10 +68,34 @@ describe('/drive/files/create', () => {
|
||||
});
|
||||
|
||||
roleService = module.get<RoleService>(RoleService);
|
||||
role_tinyAttachment = await roleService.create({
|
||||
role_imageOnly = await roleService.create({
|
||||
name: 'test-role001',
|
||||
description: 'Test role001 description',
|
||||
target: 'manual',
|
||||
policies: {
|
||||
uploadableFileTypes: {
|
||||
useDefault: false,
|
||||
priority: 1,
|
||||
value: ['image/png'],
|
||||
},
|
||||
},
|
||||
});
|
||||
role_allowAllTypes = await roleService.create({
|
||||
name: 'test-role002',
|
||||
description: 'Test role002 description',
|
||||
target: 'manual',
|
||||
policies: {
|
||||
uploadableFileTypes: {
|
||||
useDefault: false,
|
||||
priority: 1,
|
||||
value: ['*/*'],
|
||||
},
|
||||
},
|
||||
});
|
||||
role_tinyAttachment = await roleService.create({
|
||||
name: 'test-role003',
|
||||
description: 'Test role003 description',
|
||||
target: 'manual',
|
||||
policies: {
|
||||
maxFileSizeMb: {
|
||||
useDefault: false,
|
||||
@@ -82,6 +110,10 @@ describe('/drive/files/create', () => {
|
||||
beforeEach(async () => {
|
||||
await roleService.unassign(root.id, role_tinyAttachment.id).catch(() => {
|
||||
});
|
||||
await roleService.unassign(root.id, role_imageOnly.id).catch(() => {
|
||||
});
|
||||
await roleService.unassign(root.id, role_allowAllTypes.id).catch(() => {
|
||||
});
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
@@ -110,7 +142,9 @@ describe('/drive/files/create', () => {
|
||||
.field('i', root.token ?? '');
|
||||
}
|
||||
|
||||
test('200 ok', async () => {
|
||||
test('200 ok (all types allowed)', async () => {
|
||||
await roleService.assign(root.id, role_allowAllTypes.id);
|
||||
|
||||
const name = randomString();
|
||||
const comment = randomString();
|
||||
const result = await postFile({
|
||||
@@ -127,7 +161,24 @@ describe('/drive/files/create', () => {
|
||||
expect(result.body.folderId).toBe(folder.id);
|
||||
});
|
||||
|
||||
test('200 ok(with role)', async () => {
|
||||
test('400 when not allowed type', async () => {
|
||||
await roleService.assign(root.id, role_imageOnly.id);
|
||||
|
||||
const name = randomString();
|
||||
const comment = randomString();
|
||||
const result = await postFile({
|
||||
name: name,
|
||||
comment: comment,
|
||||
isSensitive: true,
|
||||
force: true,
|
||||
fileContent: Buffer.from('a'.repeat(10)),
|
||||
});
|
||||
expect(result.statusCode).toBe(400);
|
||||
expect(result.body.error.code).toBe('UNALLOWED_FILE_TYPE');
|
||||
});
|
||||
|
||||
test('200 ok (with size limited role)', async () => {
|
||||
await roleService.assign(root.id, role_allowAllTypes.id);
|
||||
await roleService.assign(root.id, role_tinyAttachment.id);
|
||||
|
||||
const name = randomString();
|
||||
@@ -147,6 +198,7 @@ describe('/drive/files/create', () => {
|
||||
});
|
||||
|
||||
test('413 too large', async () => {
|
||||
await roleService.assign(root.id, role_allowAllTypes.id);
|
||||
await roleService.assign(root.id, role_tinyAttachment.id);
|
||||
|
||||
const name = randomString();
|
||||
|
||||
Reference in New Issue
Block a user