;
@@ -226,7 +232,7 @@ export type SchemaTypeDef =
p['items']['allOf'] extends ReadonlyArray ? UnionToIntersection>>[] :
never
) :
- p['items'] extends NonNullable ? SchemaTypeDef[] :
+ p['items'] extends NonNullable ? SchemaType[] :
any[]
) :
p['anyOf'] extends ReadonlyArray ? UnionSchemaType & PartialIntersection> :
diff --git a/packages/backend/src/misc/loader.ts b/packages/backend/src/misc/loader.ts
index 25f7b54d31..7f29b9db10 100644
--- a/packages/backend/src/misc/loader.ts
+++ b/packages/backend/src/misc/loader.ts
@@ -1,3 +1,8 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
export type FetchFunction = (key: K) => Promise;
type ResolveReject = Parameters>[0]>;
diff --git a/packages/backend/src/misc/prelude/array.ts b/packages/backend/src/misc/prelude/array.ts
index dbfe1fff18..f741a0c913 100644
--- a/packages/backend/src/misc/prelude/array.ts
+++ b/packages/backend/src/misc/prelude/array.ts
@@ -65,44 +65,6 @@ export function maximum(xs: number[]): number {
return Math.max(...xs);
}
-/**
- * Splits an array based on the equivalence relation.
- * The concatenation of the result is equal to the argument.
- */
-export function groupBy(f: EndoRelation, xs: T[]): T[][] {
- const groups = [] as T[][];
- for (const x of xs) {
- const lastGroup = groups.at(-1);
- if (lastGroup !== undefined && f(lastGroup[0], x)) {
- lastGroup.push(x);
- } else {
- groups.push([x]);
- }
- }
- return groups;
-}
-
-/**
- * Splits an array based on the equivalence relation induced by the function.
- * The concatenation of the result is equal to the argument.
- */
-export function groupOn(f: (x: T) => S, xs: T[]): T[][] {
- return groupBy((a, b) => f(a) === f(b), xs);
-}
-
-export function groupByX(collections: T[], keySelector: (x: T) => string) {
- return collections.reduce((obj: Record, item: T) => {
- const key = keySelector(item);
- if (!Object.prototype.hasOwnProperty.call(obj, key)) {
- obj[key] = [];
- }
-
- obj[key].push(item);
-
- return obj;
- }, {});
-}
-
/**
* Compare two arrays by lexicographical order
*/
diff --git a/packages/backend/src/misc/prelude/maybe.ts b/packages/backend/src/misc/prelude/maybe.ts
deleted file mode 100644
index 1c58ccb9c7..0000000000
--- a/packages/backend/src/misc/prelude/maybe.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * SPDX-FileCopyrightText: syuilo and misskey-project
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-
-export interface IMaybe {
- isJust(): this is IJust;
-}
-
-export interface IJust extends IMaybe {
- get(): T;
-}
-
-export function just(value: T): IJust {
- return {
- isJust: () => true,
- get: () => value,
- };
-}
-
-export function nothing(): IMaybe {
- return {
- isJust: () => false,
- };
-}
diff --git a/packages/backend/src/misc/prelude/string.ts b/packages/backend/src/misc/prelude/string.ts
deleted file mode 100644
index 67ea529961..0000000000
--- a/packages/backend/src/misc/prelude/string.ts
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * SPDX-FileCopyrightText: syuilo and misskey-project
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-
-export function concat(xs: string[]): string {
- return xs.join('');
-}
-
-export function capitalize(s: string): string {
- return toUpperCase(s.charAt(0)) + toLowerCase(s.slice(1));
-}
-
-export function toUpperCase(s: string): string {
- return s.toUpperCase();
-}
-
-export function toLowerCase(s: string): string {
- return s.toLowerCase();
-}
diff --git a/packages/backend/src/models/AbuseReportNotificationRecipient.ts b/packages/backend/src/models/AbuseReportNotificationRecipient.ts
new file mode 100644
index 0000000000..fbff880afc
--- /dev/null
+++ b/packages/backend/src/models/AbuseReportNotificationRecipient.ts
@@ -0,0 +1,100 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { Column, Entity, Index, JoinColumn, ManyToOne, PrimaryColumn } from 'typeorm';
+import { MiSystemWebhook } from '@/models/SystemWebhook.js';
+import { MiUserProfile } from '@/models/UserProfile.js';
+import { id } from './util/id.js';
+import { MiUser } from './User.js';
+
+/**
+ * 通報受信時に通知を送信する方法.
+ */
+export type RecipientMethod = 'email' | 'webhook';
+
+@Entity('abuse_report_notification_recipient')
+export class MiAbuseReportNotificationRecipient {
+ @PrimaryColumn(id())
+ public id: string;
+
+ /**
+ * 有効かどうか.
+ */
+ @Index()
+ @Column('boolean', {
+ default: true,
+ })
+ public isActive: boolean;
+
+ /**
+ * 更新日時.
+ */
+ @Column('timestamp with time zone', {
+ default: () => 'CURRENT_TIMESTAMP',
+ })
+ public updatedAt: Date;
+
+ /**
+ * 通知設定名.
+ */
+ @Column('varchar', {
+ length: 255,
+ })
+ public name: string;
+
+ /**
+ * 通知方法.
+ */
+ @Index()
+ @Column('varchar', {
+ length: 64,
+ })
+ public method: RecipientMethod;
+
+ /**
+ * 通知先のユーザID.
+ */
+ @Index()
+ @Column({
+ ...id(),
+ nullable: true,
+ })
+ public userId: MiUser['id'] | null;
+
+ /**
+ * 通知先のユーザ.
+ */
+ @ManyToOne(type => MiUser, {
+ onDelete: 'CASCADE',
+ })
+ @JoinColumn({ name: 'userId', referencedColumnName: 'id', foreignKeyConstraintName: 'FK_abuse_report_notification_recipient_userId1' })
+ public user: MiUser | null;
+
+ /**
+ * 通知先のユーザプロフィール.
+ */
+ @ManyToOne(type => MiUserProfile, {})
+ @JoinColumn({ name: 'userId', referencedColumnName: 'userId', foreignKeyConstraintName: 'FK_abuse_report_notification_recipient_userId2' })
+ public userProfile: MiUserProfile | null;
+
+ /**
+ * 通知先のシステムWebhookId.
+ */
+ @Index()
+ @Column({
+ ...id(),
+ nullable: true,
+ })
+ public systemWebhookId: string | null;
+
+ /**
+ * 通知先のシステムWebhook.
+ */
+ @ManyToOne(type => MiSystemWebhook, {
+ onDelete: 'CASCADE',
+ })
+ @JoinColumn()
+ public systemWebhook: MiSystemWebhook | null;
+}
diff --git a/packages/backend/src/models/Antenna.ts b/packages/backend/src/models/Antenna.ts
index 332a899768..33e6f48189 100644
--- a/packages/backend/src/models/Antenna.ts
+++ b/packages/backend/src/models/Antenna.ts
@@ -72,6 +72,11 @@ export class MiAntenna {
})
public caseSensitive: boolean;
+ @Column('boolean', {
+ default: false,
+ })
+ public excludeBots: boolean;
+
@Column('boolean', {
default: false,
})
@@ -85,9 +90,6 @@ export class MiAntenna {
})
public expression: string | null;
- @Column('boolean')
- public notify: boolean;
-
@Index()
@Column('boolean', {
default: true,
diff --git a/packages/backend/src/models/Instance.ts b/packages/backend/src/models/Instance.ts
index 9863c9d75d..17cd5c6665 100644
--- a/packages/backend/src/models/Instance.ts
+++ b/packages/backend/src/models/Instance.ts
@@ -81,13 +81,22 @@ export class MiInstance {
public isNotResponding: boolean;
/**
- * このインスタンスへの配信を停止するか
+ * このインスタンスと不通になった日時
+ */
+ @Column('timestamp with time zone', {
+ nullable: true,
+ })
+ public notRespondingSince: Date | null;
+
+ /**
+ * このインスタンスへの配信状態
*/
@Index()
- @Column('boolean', {
- default: false,
+ @Column('enum', {
+ default: 'none',
+ enum: ['none', 'manuallySuspended', 'goneSuspended', 'autoSuspendedForNotResponding'],
})
- public isSuspended: boolean;
+ public suspensionState: 'none' | 'manuallySuspended' | 'goneSuspended' | 'autoSuspendedForNotResponding';
@Column('varchar', {
length: 64, nullable: true,
diff --git a/packages/backend/src/models/Meta.ts b/packages/backend/src/models/Meta.ts
index 66f19ce197..ad306fcad6 100644
--- a/packages/backend/src/models/Meta.ts
+++ b/packages/backend/src/models/Meta.ts
@@ -277,12 +277,6 @@ export class MiMeta {
})
public enableSensitiveMediaDetectionForVideos: boolean;
- @Column('varchar', {
- length: 1024,
- nullable: true,
- })
- public summalyProxy: string | null;
-
@Column('boolean', {
default: false,
})
@@ -382,6 +376,12 @@ export class MiMeta {
})
public privacyPolicyUrl: string | null;
+ @Column('varchar', {
+ length: 1024,
+ nullable: true,
+ })
+ public inquiryUrl: string | null;
+
@Column('varchar', {
length: 8192,
nullable: true,
@@ -588,4 +588,36 @@ export class MiMeta {
default: 0,
})
public notesPerOneAd: number;
+
+ @Column('boolean', {
+ default: true,
+ })
+ public urlPreviewEnabled: boolean;
+
+ @Column('integer', {
+ default: 10000,
+ })
+ public urlPreviewTimeout: number;
+
+ @Column('bigint', {
+ default: 1024 * 1024 * 10,
+ })
+ public urlPreviewMaximumContentLength: number;
+
+ @Column('boolean', {
+ default: true,
+ })
+ public urlPreviewRequireContentLength: boolean;
+
+ @Column('varchar', {
+ length: 1024,
+ nullable: true,
+ })
+ public urlPreviewSummaryProxyUrl: string | null;
+
+ @Column('varchar', {
+ length: 1024,
+ nullable: true,
+ })
+ public urlPreviewUserAgent: string | null;
}
diff --git a/packages/backend/src/models/Poll.ts b/packages/backend/src/models/Poll.ts
index c2693dbb19..ca985c8b24 100644
--- a/packages/backend/src/models/Poll.ts
+++ b/packages/backend/src/models/Poll.ts
@@ -8,6 +8,7 @@ import { noteVisibilities } from '@/types.js';
import { id } from './util/id.js';
import { MiNote } from './Note.js';
import type { MiUser } from './User.js';
+import type { MiChannel } from "@/models/Channel.js";
@Entity('poll')
export class MiPoll {
@@ -58,6 +59,14 @@ export class MiPoll {
comment: '[Denormalized]',
})
public userHost: string | null;
+
+ @Index()
+ @Column({
+ ...id(),
+ nullable: true,
+ comment: '[Denormalized]',
+ })
+ public channelId: MiChannel['id'] | null;
//#endregion
constructor(data: Partial) {
diff --git a/packages/backend/src/models/RepositoryModule.ts b/packages/backend/src/models/RepositoryModule.ts
index bd447570dd..ea0f88baba 100644
--- a/packages/backend/src/models/RepositoryModule.ts
+++ b/packages/backend/src/models/RepositoryModule.ts
@@ -3,417 +3,500 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
+import type { Provider } from '@nestjs/common';
import { Module } from '@nestjs/common';
import { DI } from '@/di-symbols.js';
-import { MiAbuseUserReport, MiAccessToken, MiAd, MiAnnouncement, MiAnnouncementRead, MiAntenna, MiApp, MiAuthSession, MiAvatarDecoration, MiBlocking, MiChannel, MiChannelFavorite, MiChannelFollowing, MiClip, MiClipFavorite, MiClipNote, MiDriveFile, MiDriveFolder, MiEmoji, MiFlash, MiFlashLike, MiFollowRequest, MiFollowing, MiGalleryLike, MiGalleryPost, MiHashtag, MiInstance, MiMeta, MiModerationLog, MiMuting, MiNote, MiNoteFavorite, MiNoteReaction, MiNoteThreadMuting, MiNoteUnread, MiPage, MiPageLike, MiPasswordResetRequest, MiPoll, MiPollVote, MiPromoNote, MiPromoRead, MiRegistrationTicket, MiRegistryItem, MiRelay, MiRenoteMuting, MiRetentionAggregation, MiRole, MiRoleAssignment, MiSignin, MiSwSubscription, MiUsedUsername, MiUser, MiUserIp, MiUserKeypair, MiUserList, MiUserListFavorite, MiUserListMembership, MiUserMemo, MiUserNotePining, MiUserPending, MiUserProfile, MiUserPublickey, MiUserSecurityKey, MiWebhook, MiBubbleGameRecord, MiReversiGame } from './_.js';
+import {
+ MiAbuseReportNotificationRecipient,
+ MiAbuseUserReport,
+ MiAccessToken,
+ MiAd,
+ MiAnnouncement,
+ MiAnnouncementRead,
+ MiAntenna,
+ MiApp,
+ MiAuthSession,
+ MiAvatarDecoration,
+ MiBlocking,
+ MiBubbleGameRecord,
+ MiChannel,
+ MiChannelFavorite,
+ MiChannelFollowing,
+ MiClip,
+ MiClipFavorite,
+ MiClipNote,
+ MiDriveFile,
+ MiDriveFolder,
+ MiEmoji,
+ MiFlash,
+ MiFlashLike,
+ MiFollowing,
+ MiFollowRequest,
+ MiGalleryLike,
+ MiGalleryPost,
+ MiHashtag,
+ MiInstance,
+ MiMeta,
+ MiModerationLog,
+ MiMuting,
+ MiNote,
+ MiNoteFavorite,
+ MiNoteReaction,
+ MiNoteThreadMuting,
+ MiNoteUnread,
+ MiPage,
+ MiPageLike,
+ MiPasswordResetRequest,
+ MiPoll,
+ MiPollVote,
+ MiPromoNote,
+ MiPromoRead,
+ MiRegistrationTicket,
+ MiRegistryItem,
+ MiRelay,
+ MiRenoteMuting,
+ MiRepository,
+ miRepository,
+ MiRetentionAggregation,
+ MiReversiGame,
+ MiRole,
+ MiRoleAssignment,
+ MiSignin,
+ MiSwSubscription,
+ MiSystemWebhook,
+ MiUsedUsername,
+ MiUser,
+ MiUserIp,
+ MiUserKeypair,
+ MiUserList,
+ MiUserListFavorite,
+ MiUserListMembership,
+ MiUserMemo,
+ MiUserNotePining,
+ MiUserPending,
+ MiUserProfile,
+ MiUserPublickey,
+ MiUserSecurityKey,
+ MiWebhook
+} from './_.js';
import type { DataSource } from 'typeorm';
-import type { Provider } from '@nestjs/common';
const $usersRepository: Provider = {
provide: DI.usersRepository,
- useFactory: (db: DataSource) => db.getRepository(MiUser),
+ useFactory: (db: DataSource) => db.getRepository(MiUser).extend(miRepository as MiRepository),
inject: [DI.db],
};
const $notesRepository: Provider = {
provide: DI.notesRepository,
- useFactory: (db: DataSource) => db.getRepository(MiNote),
+ useFactory: (db: DataSource) => db.getRepository(MiNote).extend(miRepository as MiRepository),
inject: [DI.db],
};
const $announcementsRepository: Provider = {
provide: DI.announcementsRepository,
- useFactory: (db: DataSource) => db.getRepository(MiAnnouncement),
+ useFactory: (db: DataSource) => db.getRepository(MiAnnouncement).extend(miRepository as MiRepository),
inject: [DI.db],
};
const $announcementReadsRepository: Provider = {
provide: DI.announcementReadsRepository,
- useFactory: (db: DataSource) => db.getRepository(MiAnnouncementRead),
+ useFactory: (db: DataSource) => db.getRepository(MiAnnouncementRead).extend(miRepository as MiRepository),
inject: [DI.db],
};
const $appsRepository: Provider = {
provide: DI.appsRepository,
- useFactory: (db: DataSource) => db.getRepository(MiApp),
+ useFactory: (db: DataSource) => db.getRepository(MiApp).extend(miRepository as MiRepository),
inject: [DI.db],
};
const $avatarDecorationsRepository: Provider = {
provide: DI.avatarDecorationsRepository,
- useFactory: (db: DataSource) => db.getRepository(MiAvatarDecoration),
+ useFactory: (db: DataSource) => db.getRepository(MiAvatarDecoration).extend(miRepository as MiRepository),
inject: [DI.db],
};
const $noteFavoritesRepository: Provider = {
provide: DI.noteFavoritesRepository,
- useFactory: (db: DataSource) => db.getRepository(MiNoteFavorite),
+ useFactory: (db: DataSource) => db.getRepository(MiNoteFavorite).extend(miRepository as MiRepository),
inject: [DI.db],
};
const $noteThreadMutingsRepository: Provider = {
provide: DI.noteThreadMutingsRepository,
- useFactory: (db: DataSource) => db.getRepository(MiNoteThreadMuting),
+ useFactory: (db: DataSource) => db.getRepository(MiNoteThreadMuting).extend(miRepository as MiRepository),
inject: [DI.db],
};
const $noteReactionsRepository: Provider = {
provide: DI.noteReactionsRepository,
- useFactory: (db: DataSource) => db.getRepository(MiNoteReaction),
+ useFactory: (db: DataSource) => db.getRepository(MiNoteReaction).extend(miRepository as MiRepository),
inject: [DI.db],
};
const $noteUnreadsRepository: Provider = {
provide: DI.noteUnreadsRepository,
- useFactory: (db: DataSource) => db.getRepository(MiNoteUnread),
+ useFactory: (db: DataSource) => db.getRepository(MiNoteUnread).extend(miRepository as MiRepository),
inject: [DI.db],
};
const $pollsRepository: Provider = {
provide: DI.pollsRepository,
- useFactory: (db: DataSource) => db.getRepository(MiPoll),
+ useFactory: (db: DataSource) => db.getRepository(MiPoll).extend(miRepository as MiRepository),
inject: [DI.db],
};
const $pollVotesRepository: Provider = {
provide: DI.pollVotesRepository,
- useFactory: (db: DataSource) => db.getRepository(MiPollVote),
+ useFactory: (db: DataSource) => db.getRepository(MiPollVote).extend(miRepository as MiRepository),
inject: [DI.db],
};
const $userProfilesRepository: Provider = {
provide: DI.userProfilesRepository,
- useFactory: (db: DataSource) => db.getRepository(MiUserProfile),
+ useFactory: (db: DataSource) => db.getRepository(MiUserProfile).extend(miRepository as MiRepository),
inject: [DI.db],
};
const $userKeypairsRepository: Provider = {
provide: DI.userKeypairsRepository,
- useFactory: (db: DataSource) => db.getRepository(MiUserKeypair),
+ useFactory: (db: DataSource) => db.getRepository(MiUserKeypair).extend(miRepository as MiRepository),
inject: [DI.db],
};
const $userPendingsRepository: Provider = {
provide: DI.userPendingsRepository,
- useFactory: (db: DataSource) => db.getRepository(MiUserPending),
+ useFactory: (db: DataSource) => db.getRepository(MiUserPending).extend(miRepository as MiRepository),
inject: [DI.db],
};
const $userSecurityKeysRepository: Provider = {
provide: DI.userSecurityKeysRepository,
- useFactory: (db: DataSource) => db.getRepository(MiUserSecurityKey),
+ useFactory: (db: DataSource) => db.getRepository(MiUserSecurityKey).extend(miRepository as MiRepository),
inject: [DI.db],
};
const $userPublickeysRepository: Provider = {
provide: DI.userPublickeysRepository,
- useFactory: (db: DataSource) => db.getRepository(MiUserPublickey),
+ useFactory: (db: DataSource) => db.getRepository(MiUserPublickey).extend(miRepository as MiRepository),
inject: [DI.db],
};
const $userListsRepository: Provider = {
provide: DI.userListsRepository,
- useFactory: (db: DataSource) => db.getRepository(MiUserList),
+ useFactory: (db: DataSource) => db.getRepository(MiUserList).extend(miRepository as MiRepository),
inject: [DI.db],
};
const $userListFavoritesRepository: Provider = {
provide: DI.userListFavoritesRepository,
- useFactory: (db: DataSource) => db.getRepository(MiUserListFavorite),
+ useFactory: (db: DataSource) => db.getRepository(MiUserListFavorite).extend(miRepository as MiRepository),
inject: [DI.db],
};
const $userListMembershipsRepository: Provider = {
provide: DI.userListMembershipsRepository,
- useFactory: (db: DataSource) => db.getRepository(MiUserListMembership),
+ useFactory: (db: DataSource) => db.getRepository(MiUserListMembership).extend(miRepository as MiRepository),
inject: [DI.db],
};
const $userNotePiningsRepository: Provider = {
provide: DI.userNotePiningsRepository,
- useFactory: (db: DataSource) => db.getRepository(MiUserNotePining),
+ useFactory: (db: DataSource) => db.getRepository(MiUserNotePining).extend(miRepository as MiRepository),
inject: [DI.db],
};
const $userIpsRepository: Provider = {
provide: DI.userIpsRepository,
- useFactory: (db: DataSource) => db.getRepository(MiUserIp),
+ useFactory: (db: DataSource) => db.getRepository(MiUserIp).extend(miRepository as MiRepository),
inject: [DI.db],
};
const $usedUsernamesRepository: Provider = {
provide: DI.usedUsernamesRepository,
- useFactory: (db: DataSource) => db.getRepository(MiUsedUsername),
+ useFactory: (db: DataSource) => db.getRepository(MiUsedUsername).extend(miRepository as MiRepository),
inject: [DI.db],
};
const $followingsRepository: Provider = {
provide: DI.followingsRepository,
- useFactory: (db: DataSource) => db.getRepository(MiFollowing),
+ useFactory: (db: DataSource) => db.getRepository(MiFollowing).extend(miRepository as MiRepository),
inject: [DI.db],
};
const $followRequestsRepository: Provider = {
provide: DI.followRequestsRepository,
- useFactory: (db: DataSource) => db.getRepository(MiFollowRequest),
+ useFactory: (db: DataSource) => db.getRepository(MiFollowRequest).extend(miRepository as MiRepository