From 5eff4da27b382226bcd0be0772dac6105ade4216 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=80=81=E5=91=A8=E9=83=A8=E8=90=BD?= Date: Sat, 13 Apr 2024 08:59:11 +0800 Subject: [PATCH 01/16] feat: antenna limit --- locales/en-US.yml | 1 + locales/zh-CN.yml | 1 + packages/backend-rs/index.d.ts | 1 + packages/backend-rs/src/model/entity/meta.rs | 2 ++ .../migration/1712937600000-antennaLimit.ts | 19 +++++++++++++++++++ packages/backend/src/models/entities/meta.ts | 6 ++++++ .../src/server/api/endpoints/admin/meta.ts | 6 ++++++ .../server/api/endpoints/admin/update-meta.ts | 5 +++++ .../server/api/endpoints/antennas/create.ts | 5 ++++- .../backend/src/server/api/endpoints/meta.ts | 6 ++++++ packages/client/src/pages/admin/settings.vue | 16 ++++++++++++++++ packages/firefish-js/src/entities.ts | 1 + 12 files changed, 68 insertions(+), 1 deletion(-) create mode 100644 packages/backend/src/migration/1712937600000-antennaLimit.ts diff --git a/locales/en-US.yml b/locales/en-US.yml index f8da18450c..a8663375a8 100644 --- a/locales/en-US.yml +++ b/locales/en-US.yml @@ -394,6 +394,7 @@ enableRegistration: "Enable new user registration" invite: "Invite" driveCapacityPerLocalAccount: "Drive capacity per local user" driveCapacityPerRemoteAccount: "Drive capacity per remote user" +antennaLimit: "The maximum number of antennas that each user can create" inMb: "In megabytes" iconUrl: "Icon URL" bannerUrl: "Banner image URL" diff --git a/locales/zh-CN.yml b/locales/zh-CN.yml index a8510612dc..9e7a0ed19e 100644 --- a/locales/zh-CN.yml +++ b/locales/zh-CN.yml @@ -340,6 +340,7 @@ invite: "邀请" driveCapacityPerLocalAccount: "每个本地用户的网盘容量" driveCapacityPerRemoteAccount: "每个远程用户的网盘容量" inMb: "以兆字节 (MegaByte) 为单位" +antennaLimit: "每个用户最多可以创建的天线数量" iconUrl: "图标 URL" bannerUrl: "横幅图 URL" backgroundImageUrl: "背景图 URL" diff --git a/packages/backend-rs/index.d.ts b/packages/backend-rs/index.d.ts index 617bedf5b9..509c3d912d 100644 --- a/packages/backend-rs/index.d.ts +++ b/packages/backend-rs/index.d.ts @@ -443,6 +443,7 @@ export interface Meta { recaptchaSecretKey: string | null localDriveCapacityMb: number remoteDriveCapacityMb: number + antennaLimit: number summalyProxy: string | null enableEmail: boolean email: string | null diff --git a/packages/backend-rs/src/model/entity/meta.rs b/packages/backend-rs/src/model/entity/meta.rs index b9a89914bd..7d0972b844 100644 --- a/packages/backend-rs/src/model/entity/meta.rs +++ b/packages/backend-rs/src/model/entity/meta.rs @@ -50,6 +50,8 @@ pub struct Model { pub local_drive_capacity_mb: i32, #[sea_orm(column_name = "remoteDriveCapacityMb")] pub remote_drive_capacity_mb: i32, + #[sea_orm(column_name = "antennaLimit")] + pub antenna_limit: i32, #[sea_orm(column_name = "summalyProxy")] pub summaly_proxy: Option, #[sea_orm(column_name = "enableEmail")] diff --git a/packages/backend/src/migration/1712937600000-antennaLimit.ts b/packages/backend/src/migration/1712937600000-antennaLimit.ts new file mode 100644 index 0000000000..cd8f9ff658 --- /dev/null +++ b/packages/backend/src/migration/1712937600000-antennaLimit.ts @@ -0,0 +1,19 @@ +import type { MigrationInterface, QueryRunner } from "typeorm"; + +export class antennaLimit1712937600000 implements MigrationInterface { + async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE "meta" ADD "antennaLimit" integer NOT NULL DEFAULT 5`, + undefined, + ); + await queryRunner.query( + `COMMENT ON COLUMN "meta"."antennaLimit" IS 'Antenna Limit'`, + ); + } + async down(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE "meta" DROP COLUMN "antennaLimit"`, + undefined, + ); + } +} diff --git a/packages/backend/src/models/entities/meta.ts b/packages/backend/src/models/entities/meta.ts index cdb8e14c3f..5e267a8e24 100644 --- a/packages/backend/src/models/entities/meta.ts +++ b/packages/backend/src/models/entities/meta.ts @@ -276,6 +276,12 @@ export class Meta { }) public remoteDriveCapacityMb: number; + @Column("integer", { + default: 5, + comment: "Antenna Limit", + }) + public antennaLimit: number; + @Column("varchar", { length: 128, nullable: true, diff --git a/packages/backend/src/server/api/endpoints/admin/meta.ts b/packages/backend/src/server/api/endpoints/admin/meta.ts index a22fbab8f1..1f382f1254 100644 --- a/packages/backend/src/server/api/endpoints/admin/meta.ts +++ b/packages/backend/src/server/api/endpoints/admin/meta.ts @@ -24,6 +24,11 @@ export const meta = { optional: false, nullable: false, }, + antennaLimit: { + type: "number", + optional: false, + nullable: false, + }, cacheRemoteFiles: { type: "boolean", optional: false, @@ -487,6 +492,7 @@ export default define(meta, paramDef, async () => { enableGuestTimeline: instance.enableGuestTimeline, driveCapacityPerLocalUserMb: instance.localDriveCapacityMb, driveCapacityPerRemoteUserMb: instance.remoteDriveCapacityMb, + antennaLimit: instance.antennaLimit, emailRequiredForSignup: instance.emailRequiredForSignup, enableHcaptcha: instance.enableHcaptcha, hcaptchaSiteKey: instance.hcaptchaSiteKey, diff --git a/packages/backend/src/server/api/endpoints/admin/update-meta.ts b/packages/backend/src/server/api/endpoints/admin/update-meta.ts index 604ef3a0fc..e5234ea720 100644 --- a/packages/backend/src/server/api/endpoints/admin/update-meta.ts +++ b/packages/backend/src/server/api/endpoints/admin/update-meta.ts @@ -94,6 +94,7 @@ export const paramDef = { defaultDarkTheme: { type: "string", nullable: true }, localDriveCapacityMb: { type: "integer" }, remoteDriveCapacityMb: { type: "integer" }, + antennaLimit: { type: "integer" }, cacheRemoteFiles: { type: "boolean" }, markLocalFilesNsfwByDefault: { type: "boolean" }, emailRequiredForSignup: { type: "boolean" }, @@ -327,6 +328,10 @@ export default define(meta, paramDef, async (ps, me) => { set.remoteDriveCapacityMb = ps.remoteDriveCapacityMb; } + if (ps.antennaLimit !== undefined) { + set.antennaLimit = ps.antennaLimit; + } + if (ps.cacheRemoteFiles !== undefined) { set.cacheRemoteFiles = ps.cacheRemoteFiles; } diff --git a/packages/backend/src/server/api/endpoints/antennas/create.ts b/packages/backend/src/server/api/endpoints/antennas/create.ts index 792301d4de..792c5e30b4 100644 --- a/packages/backend/src/server/api/endpoints/antennas/create.ts +++ b/packages/backend/src/server/api/endpoints/antennas/create.ts @@ -3,6 +3,7 @@ import { genId } from "backend-rs"; import { Antennas, UserLists, UserGroupJoinings } from "@/models/index.js"; import { ApiError } from "@/server/api/error.js"; import { publishInternalEvent } from "@/services/stream.js"; +import { fetchMeta } from "@/misc/fetch-meta.js"; export const meta = { tags: ["antennas"], @@ -109,10 +110,12 @@ export default define(meta, paramDef, async (ps, user) => { let userList; let userGroupJoining; + const instance = await fetchMeta(true); + const antennas = await Antennas.findBy({ userId: user.id, }); - if (antennas.length > 5 && !user.isAdmin) { + if (antennas.length >= instance.antennaLimit) { throw new ApiError(meta.errors.tooManyAntennas); } diff --git a/packages/backend/src/server/api/endpoints/meta.ts b/packages/backend/src/server/api/endpoints/meta.ts index 2a674b52c3..cb494bf27e 100644 --- a/packages/backend/src/server/api/endpoints/meta.ts +++ b/packages/backend/src/server/api/endpoints/meta.ts @@ -126,6 +126,11 @@ export const meta = { optional: false, nullable: false, }, + antennaLimit: { + type: "number", + optional: false, + nullable: false, + }, cacheRemoteFiles: { type: "boolean", optional: false, @@ -445,6 +450,7 @@ export default define(meta, paramDef, async (ps, me) => { enableGuestTimeline: instance.enableGuestTimeline, driveCapacityPerLocalUserMb: instance.localDriveCapacityMb, driveCapacityPerRemoteUserMb: instance.remoteDriveCapacityMb, + antennaLimit: instance.antennaLimit, emailRequiredForSignup: instance.emailRequiredForSignup, enableHcaptcha: instance.enableHcaptcha, hcaptchaSiteKey: instance.hcaptchaSiteKey, diff --git a/packages/client/src/pages/admin/settings.vue b/packages/client/src/pages/admin/settings.vue index f69c4fb23b..1c2188daac 100644 --- a/packages/client/src/pages/admin/settings.vue +++ b/packages/client/src/pages/admin/settings.vue @@ -350,6 +350,19 @@ + + + + + + + @@ -502,6 +515,7 @@ const cacheRemoteFiles = ref(false); const markLocalFilesNsfwByDefault = ref(false); const localDriveCapacityMb = ref(0); const remoteDriveCapacityMb = ref(0); +const antennaLimit = ref(0); const enableRegistration = ref(false); const emailRequiredForSignup = ref(false); const enableServiceWorker = ref(false); @@ -579,6 +593,7 @@ async function init() { markLocalFilesNsfwByDefault.value = meta.markLocalFilesNsfwByDefault; localDriveCapacityMb.value = meta.driveCapacityPerLocalUserMb; remoteDriveCapacityMb.value = meta.driveCapacityPerRemoteUserMb; + antennaLimit.value = meta.antennaLimit; enableRegistration.value = !meta.disableRegistration; emailRequiredForSignup.value = meta.emailRequiredForSignup; enableServiceWorker.value = meta.enableServiceWorker; @@ -631,6 +646,7 @@ function save() { markLocalFilesNsfwByDefault: markLocalFilesNsfwByDefault.value, localDriveCapacityMb: localDriveCapacityMb.value, remoteDriveCapacityMb: remoteDriveCapacityMb.value, + antennaLimit: antennaLimit.value, disableRegistration: !enableRegistration.value, emailRequiredForSignup: emailRequiredForSignup.value, enableServiceWorker: enableServiceWorker.value, diff --git a/packages/firefish-js/src/entities.ts b/packages/firefish-js/src/entities.ts index ce940a1481..6f9d6b400b 100644 --- a/packages/firefish-js/src/entities.ts +++ b/packages/firefish-js/src/entities.ts @@ -341,6 +341,7 @@ export type LiteInstanceMetadata = { disableGlobalTimeline: boolean; driveCapacityPerLocalUserMb: number; driveCapacityPerRemoteUserMb: number; + antennaLimit: number; enableHcaptcha: boolean; hcaptchaSiteKey: string | null; enableRecaptcha: boolean; From 4823abd3a9415cea56f703854e8ea344a65f644b Mon Sep 17 00:00:00 2001 From: yumeko Date: Fri, 19 Apr 2024 03:34:25 +0300 Subject: [PATCH 02/16] Add usageHint field to DriveFile, and fill accordingly when operating on Persons --- .../migration/1713451569342-AddDriveFileUsage.ts | 15 +++++++++++++++ .../backend/src/models/entities/drive-file.ts | 7 +++++++ .../src/remote/activitypub/models/image.ts | 5 ++++- .../backend/src/remote/activitypub/models/note.ts | 4 ++-- .../src/remote/activitypub/models/person.ts | 8 ++++---- packages/backend/src/services/drive/add-file.ts | 10 ++++++++++ .../backend/src/services/drive/upload-from-url.ts | 11 +++++++++-- 7 files changed, 51 insertions(+), 9 deletions(-) create mode 100644 packages/backend/src/migration/1713451569342-AddDriveFileUsage.ts diff --git a/packages/backend/src/migration/1713451569342-AddDriveFileUsage.ts b/packages/backend/src/migration/1713451569342-AddDriveFileUsage.ts new file mode 100644 index 0000000000..c0c96fe74c --- /dev/null +++ b/packages/backend/src/migration/1713451569342-AddDriveFileUsage.ts @@ -0,0 +1,15 @@ +import type { MigrationInterface, QueryRunner } from "typeorm"; + +export class AddDriveFileUsage1713451569342 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE "drive_file" ADD "usageHint" character varying(16) DEFAULT NULL`, + ); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE "drive_file" DROP COLUMN "usageHint"` + ); + } +} diff --git a/packages/backend/src/models/entities/drive-file.ts b/packages/backend/src/models/entities/drive-file.ts index 3c49e89fd5..b5717d62cd 100644 --- a/packages/backend/src/models/entities/drive-file.ts +++ b/packages/backend/src/models/entities/drive-file.ts @@ -177,6 +177,13 @@ export class DriveFile { }) public isSensitive: boolean; + @Column("varchar", { + length: 16, + nullable: true, + comment: "Hint for what the file is used for.", + }) + public usageHint: string | null; + /** * 外部の(信頼されていない)URLへの直リンクか否か */ diff --git a/packages/backend/src/remote/activitypub/models/image.ts b/packages/backend/src/remote/activitypub/models/image.ts index e2072a963a..23fb720362 100644 --- a/packages/backend/src/remote/activitypub/models/image.ts +++ b/packages/backend/src/remote/activitypub/models/image.ts @@ -16,6 +16,7 @@ const logger = apLogger; export async function createImage( actor: CacheableRemoteUser, value: any, + usage: 'avatar' | 'banner' | null ): Promise { // Skip if author is frozen. if (actor.isSuspended) { @@ -43,6 +44,7 @@ export async function createImage( sensitive: image.sensitive, isLink: !instance.cacheRemoteFiles, comment: truncate(image.name, DB_MAX_IMAGE_COMMENT_LENGTH), + usageHint: usage }); if (file.isLink) { @@ -73,9 +75,10 @@ export async function createImage( export async function resolveImage( actor: CacheableRemoteUser, value: any, + usage: 'avatar' | 'banner' | null, ): Promise { // TODO // Fetch from remote server and register - return await createImage(actor, value); + return await createImage(actor, value, usage); } diff --git a/packages/backend/src/remote/activitypub/models/note.ts b/packages/backend/src/remote/activitypub/models/note.ts index ad59930457..5e1c3829a7 100644 --- a/packages/backend/src/remote/activitypub/models/note.ts +++ b/packages/backend/src/remote/activitypub/models/note.ts @@ -213,7 +213,7 @@ export async function createNote( ? ( await Promise.all( note.attachment.map( - (x) => limit(() => resolveImage(actor, x)) as Promise, + (x) => limit(() => resolveImage(actor, x, null)) as Promise, ), ) ).filter((image) => image != null) @@ -616,7 +616,7 @@ export async function updateNote(value: string | IObject, resolver?: Resolver) { fileList.map( (x) => limit(async () => { - const file = await resolveImage(actor, x); + const file = await resolveImage(actor, x, null); const update: Partial = {}; const altText = truncate(x.name, DB_MAX_IMAGE_COMMENT_LENGTH); diff --git a/packages/backend/src/remote/activitypub/models/person.ts b/packages/backend/src/remote/activitypub/models/person.ts index e91280125f..47a1152c36 100644 --- a/packages/backend/src/remote/activitypub/models/person.ts +++ b/packages/backend/src/remote/activitypub/models/person.ts @@ -362,10 +362,10 @@ export async function createPerson( //#region Fetch avatar and header image const [avatar, banner] = await Promise.all( - [person.icon, person.image].map((img) => + [person.icon, person.image].map((img, index) => img == null ? Promise.resolve(null) - : resolveImage(user!, img).catch(() => null), + : resolveImage(user!, img, index === 0 ? "avatar" : index === 1 ? "banner" : null).catch(() => null), ), ); @@ -438,10 +438,10 @@ export async function updatePerson( // Fetch avatar and header image const [avatar, banner] = await Promise.all( - [person.icon, person.image].map((img) => + [person.icon, person.image].map((img, index) => img == null ? Promise.resolve(null) - : resolveImage(user, img).catch(() => null), + : resolveImage(user, img, index === 0 ? "avatar" : index === 1 ? "banner" : null).catch(() => null), ), ); diff --git a/packages/backend/src/services/drive/add-file.ts b/packages/backend/src/services/drive/add-file.ts index 24ad9f8f02..cfb871dd93 100644 --- a/packages/backend/src/services/drive/add-file.ts +++ b/packages/backend/src/services/drive/add-file.ts @@ -65,6 +65,7 @@ function urlPathJoin( * @param type Content-Type for original * @param hash Hash for original * @param size Size for original + * @param usage Optional usage hint for file (f.e. "avatar") */ async function save( file: DriveFile, @@ -73,6 +74,7 @@ async function save( type: string, hash: string, size: number, + usage: string | null = null ): Promise { // thunbnail, webpublic を必要なら生成 const alts = await generateAlts(path, type, !file.uri); @@ -161,6 +163,7 @@ async function save( file.md5 = hash; file.size = size; file.storedInternal = false; + file.usageHint = usage ?? null; return await DriveFiles.insert(file).then((x) => DriveFiles.findOneByOrFail(x.identifiers[0]), @@ -204,6 +207,7 @@ async function save( file.type = type; file.md5 = hash; file.size = size; + file.usageHint = usage ?? null; return await DriveFiles.insert(file).then((x) => DriveFiles.findOneByOrFail(x.identifiers[0]), @@ -450,6 +454,9 @@ type AddFileArgs = { requestIp?: string | null; requestHeaders?: Record | null; + + /** Whether this file has a known use case, like user avatar or instance icon */ + usageHint?: string | null; }; /** @@ -469,6 +476,7 @@ export async function addFile({ sensitive = null, requestIp = null, requestHeaders = null, + usageHint = null, }: AddFileArgs): Promise { const info = await getFileInfo(path); logger.info(`${JSON.stringify(info)}`); @@ -581,6 +589,7 @@ export async function addFile({ file.isLink = isLink; file.requestIp = requestIp; file.requestHeaders = requestHeaders; + file.usageHint = usageHint; file.isSensitive = user ? Users.isLocalUser(user) && (instance!.markLocalFilesNsfwByDefault || profile!.alwaysMarkNsfw) @@ -639,6 +648,7 @@ export async function addFile({ info.type.mime, info.md5, info.size, + usageHint ); } diff --git a/packages/backend/src/services/drive/upload-from-url.ts b/packages/backend/src/services/drive/upload-from-url.ts index 551d3757ca..238f8714fa 100644 --- a/packages/backend/src/services/drive/upload-from-url.ts +++ b/packages/backend/src/services/drive/upload-from-url.ts @@ -13,7 +13,11 @@ const logger = driveLogger.createSubLogger("downloader"); type Args = { url: string; - user: { id: User["id"]; host: User["host"] } | null; + user: { + id: User["id"]; + host: User["host"]; + driveCapacityOverrideMb: User["driveCapacityOverrideMb"]; + } | null; folderId?: DriveFolder["id"] | null; uri?: string | null; sensitive?: boolean; @@ -22,6 +26,7 @@ type Args = { comment?: string | null; requestIp?: string | null; requestHeaders?: Record | null; + usageHint?: string | null; }; export async function uploadFromUrl({ @@ -35,6 +40,7 @@ export async function uploadFromUrl({ comment = null, requestIp = null, requestHeaders = null, + usageHint = null }: Args): Promise { const parsedUrl = new URL(url); if ( @@ -75,9 +81,10 @@ export async function uploadFromUrl({ sensitive, requestIp, requestHeaders, + usageHint }); logger.succ(`Got: ${driveFile.id}`); - return driveFile!; + return driveFile; } catch (e) { logger.error(`Failed to create drive file:\n${inspect(e)}`); throw e; From c0f93de94b2183c53ce041b58d028474c0f7ed73 Mon Sep 17 00:00:00 2001 From: yumeko Date: Fri, 19 Apr 2024 06:26:40 +0300 Subject: [PATCH 03/16] Set file usage hints on local avatar/banner uploads as well + export "valid" values as type --- .../backend/src/models/entities/drive-file.ts | 4 +++- .../src/remote/activitypub/models/image.ts | 6 ++--- .../src/remote/activitypub/models/person.ts | 4 ++-- .../src/server/api/endpoints/i/update.ts | 23 +++++++++++++++++-- .../backend/src/services/drive/add-file.ts | 7 +++--- 5 files changed, 33 insertions(+), 11 deletions(-) diff --git a/packages/backend/src/models/entities/drive-file.ts b/packages/backend/src/models/entities/drive-file.ts index b5717d62cd..f257280d60 100644 --- a/packages/backend/src/models/entities/drive-file.ts +++ b/packages/backend/src/models/entities/drive-file.ts @@ -16,6 +16,8 @@ import { DriveFolder } from "./drive-folder.js"; import { DB_MAX_IMAGE_COMMENT_LENGTH } from "@/misc/hard-limits.js"; import { NoteFile } from "./note-file.js"; +export type DriveFileUsageHint = "user_avatar" | "user_banner" | null; + @Entity() @Index(["userId", "folderId", "id"]) export class DriveFile { @@ -182,7 +184,7 @@ export class DriveFile { nullable: true, comment: "Hint for what the file is used for.", }) - public usageHint: string | null; + public usageHint: DriveFileUsageHint; /** * 外部の(信頼されていない)URLへの直リンクか否か diff --git a/packages/backend/src/remote/activitypub/models/image.ts b/packages/backend/src/remote/activitypub/models/image.ts index 23fb720362..9183b82ebe 100644 --- a/packages/backend/src/remote/activitypub/models/image.ts +++ b/packages/backend/src/remote/activitypub/models/image.ts @@ -3,7 +3,7 @@ import type { CacheableRemoteUser } from "@/models/entities/user.js"; import Resolver from "../resolver.js"; import { fetchMeta } from "backend-rs"; import { apLogger } from "../logger.js"; -import type { DriveFile } from "@/models/entities/drive-file.js"; +import type { DriveFile, DriveFileUsageHint } from "@/models/entities/drive-file.js"; import { DriveFiles } from "@/models/index.js"; import { truncate } from "@/misc/truncate.js"; import { DB_MAX_IMAGE_COMMENT_LENGTH } from "@/misc/hard-limits.js"; @@ -16,7 +16,7 @@ const logger = apLogger; export async function createImage( actor: CacheableRemoteUser, value: any, - usage: 'avatar' | 'banner' | null + usage: DriveFileUsageHint ): Promise { // Skip if author is frozen. if (actor.isSuspended) { @@ -75,7 +75,7 @@ export async function createImage( export async function resolveImage( actor: CacheableRemoteUser, value: any, - usage: 'avatar' | 'banner' | null, + usage: DriveFileUsageHint, ): Promise { // TODO diff --git a/packages/backend/src/remote/activitypub/models/person.ts b/packages/backend/src/remote/activitypub/models/person.ts index 47a1152c36..64a4c7f5c5 100644 --- a/packages/backend/src/remote/activitypub/models/person.ts +++ b/packages/backend/src/remote/activitypub/models/person.ts @@ -365,7 +365,7 @@ export async function createPerson( [person.icon, person.image].map((img, index) => img == null ? Promise.resolve(null) - : resolveImage(user!, img, index === 0 ? "avatar" : index === 1 ? "banner" : null).catch(() => null), + : resolveImage(user, img, index === 0 ? "user_avatar" : index === 1 ? "user_banner" : null).catch(() => null) ), ); @@ -441,7 +441,7 @@ export async function updatePerson( [person.icon, person.image].map((img, index) => img == null ? Promise.resolve(null) - : resolveImage(user, img, index === 0 ? "avatar" : index === 1 ? "banner" : null).catch(() => null), + : resolveImage(user, img, index === 0 ? "user_avatar" : index === 1 ? "user_banner" : null).catch(() => null), ), ); diff --git a/packages/backend/src/server/api/endpoints/i/update.ts b/packages/backend/src/server/api/endpoints/i/update.ts index 4389688a12..9d39903eac 100644 --- a/packages/backend/src/server/api/endpoints/i/update.ts +++ b/packages/backend/src/server/api/endpoints/i/update.ts @@ -13,6 +13,7 @@ import { normalizeForSearch } from "@/misc/normalize-for-search.js"; import { verifyLink } from "@/services/fetch-rel-me.js"; import { ApiError } from "@/server/api/error.js"; import define from "@/server/api/define.js"; +import { DriveFile } from "@/models/entities/drive-file"; export const meta = { tags: ["account"], @@ -241,8 +242,9 @@ export default define(meta, paramDef, async (ps, _user, token) => { if (ps.emailNotificationTypes !== undefined) profileUpdates.emailNotificationTypes = ps.emailNotificationTypes; + let avatar: DriveFile | null = null if (ps.avatarId) { - const avatar = await DriveFiles.findOneBy({ id: ps.avatarId }); + avatar = await DriveFiles.findOneBy({ id: ps.avatarId }); if (avatar == null || avatar.userId !== user.id) throw new ApiError(meta.errors.noSuchAvatar); @@ -250,8 +252,9 @@ export default define(meta, paramDef, async (ps, _user, token) => { throw new ApiError(meta.errors.avatarNotAnImage); } + let banner: DriveFile | null = null if (ps.bannerId) { - const banner = await DriveFiles.findOneBy({ id: ps.bannerId }); + banner = await DriveFiles.findOneBy({ id: ps.bannerId }); if (banner == null || banner.userId !== user.id) throw new ApiError(meta.errors.noSuchBanner); @@ -328,6 +331,22 @@ export default define(meta, paramDef, async (ps, _user, token) => { updateUsertags(user, tags); //#endregion + // Update old/new avatar usage hints + if (avatar) + { + if (user.avatarId) + await DriveFiles.update(user.avatarId, {usageHint: null}); + await DriveFiles.update(avatar.id, {usageHint: "user_avatar"}); + } + + // Update old/new banner usage hints + if (banner) + { + if (user.bannerId) + await DriveFiles.update(user.bannerId, {usageHint: null}); + await DriveFiles.update(banner.id, {usageHint: "user_banner"}); + } + if (Object.keys(updates).length > 0) await Users.update(user.id, updates); if (Object.keys(profileUpdates).length > 0) await UserProfiles.update(user.id, profileUpdates); diff --git a/packages/backend/src/services/drive/add-file.ts b/packages/backend/src/services/drive/add-file.ts index cfb871dd93..79f30a651b 100644 --- a/packages/backend/src/services/drive/add-file.ts +++ b/packages/backend/src/services/drive/add-file.ts @@ -16,6 +16,7 @@ import { UserProfiles, } from "@/models/index.js"; import { DriveFile } from "@/models/entities/drive-file.js"; +import type { DriveFileUsageHint } from "@/models/entities/drive-file.js"; import type { IRemoteUser, User } from "@/models/entities/user.js"; import { genId } from "backend-rs"; import { isDuplicateKeyValueError } from "@/misc/is-duplicate-key-value-error.js"; @@ -65,7 +66,7 @@ function urlPathJoin( * @param type Content-Type for original * @param hash Hash for original * @param size Size for original - * @param usage Optional usage hint for file (f.e. "avatar") + * @param usage Optional usage hint for file (f.e. "user_avatar") */ async function save( file: DriveFile, @@ -74,7 +75,7 @@ async function save( type: string, hash: string, size: number, - usage: string | null = null + usage: DriveFileUsageHint = null ): Promise { // thunbnail, webpublic を必要なら生成 const alts = await generateAlts(path, type, !file.uri); @@ -456,7 +457,7 @@ type AddFileArgs = { requestHeaders?: Record | null; /** Whether this file has a known use case, like user avatar or instance icon */ - usageHint?: string | null; + usageHint?: DriveFileUsageHint; }; /** From 4aeb0d95ccf7e4427c60ecd915a48922f465e451 Mon Sep 17 00:00:00 2001 From: yumeko Date: Fri, 19 Apr 2024 07:03:09 +0300 Subject: [PATCH 04/16] Add DriveFile usageHint field to rust model as well --- packages/backend-rs/src/model/entity/drive_file.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/backend-rs/src/model/entity/drive_file.rs b/packages/backend-rs/src/model/entity/drive_file.rs index e3e4622a62..e5c3995573 100644 --- a/packages/backend-rs/src/model/entity/drive_file.rs +++ b/packages/backend-rs/src/model/entity/drive_file.rs @@ -52,6 +52,8 @@ pub struct Model { pub request_headers: Option, #[sea_orm(column_name = "requestIp")] pub request_ip: Option, + #[sea_orm(column_name = "usageHint")] + pub usage_hint: Option, } #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] From 913de651dbb4413728599a1c7df3a646098c750c Mon Sep 17 00:00:00 2001 From: yumeko Date: Fri, 19 Apr 2024 07:25:42 +0300 Subject: [PATCH 05/16] When updating (remote) user avatar/banner, clear usageHint for the previous drivefile, if any --- packages/backend/src/remote/activitypub/models/person.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/backend/src/remote/activitypub/models/person.ts b/packages/backend/src/remote/activitypub/models/person.ts index 64a4c7f5c5..5460a0234b 100644 --- a/packages/backend/src/remote/activitypub/models/person.ts +++ b/packages/backend/src/remote/activitypub/models/person.ts @@ -10,6 +10,7 @@ import { Followings, UserProfiles, UserPublickeys, + DriveFiles, } from "@/models/index.js"; import type { IRemoteUser, CacheableUser } from "@/models/entities/user.js"; import { User } from "@/models/entities/user.js"; @@ -561,10 +562,14 @@ export async function updatePerson( } as Partial; if (avatar) { + if (user?.avatarId) + await DriveFiles.update(user.avatarId, {usageHint: null}); updates.avatarId = avatar.id; } if (banner) { + if (user?.bannerId) + await DriveFiles.update(user.bannerId, {usageHint: null}); updates.bannerId = banner.id; } From 968657d26eff412fd18e8b8cab47718f40a0f5e1 Mon Sep 17 00:00:00 2001 From: yumeko Date: Fri, 19 Apr 2024 07:54:11 +0300 Subject: [PATCH 06/16] Run format --- .../1713451569342-AddDriveFileUsage.ts | 4 +--- .../src/remote/activitypub/models/image.ts | 9 ++++++--- .../src/remote/activitypub/models/note.ts | 3 ++- .../src/remote/activitypub/models/person.ts | 16 ++++++++++++---- .../src/server/api/endpoints/i/update.ts | 18 ++++++++---------- .../backend/src/services/drive/add-file.ts | 4 ++-- .../src/services/drive/upload-from-url.ts | 8 ++++---- 7 files changed, 35 insertions(+), 27 deletions(-) diff --git a/packages/backend/src/migration/1713451569342-AddDriveFileUsage.ts b/packages/backend/src/migration/1713451569342-AddDriveFileUsage.ts index c0c96fe74c..57c6a73890 100644 --- a/packages/backend/src/migration/1713451569342-AddDriveFileUsage.ts +++ b/packages/backend/src/migration/1713451569342-AddDriveFileUsage.ts @@ -8,8 +8,6 @@ export class AddDriveFileUsage1713451569342 implements MigrationInterface { } public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `ALTER TABLE "drive_file" DROP COLUMN "usageHint"` - ); + await queryRunner.query(`ALTER TABLE "drive_file" DROP COLUMN "usageHint"`); } } diff --git a/packages/backend/src/remote/activitypub/models/image.ts b/packages/backend/src/remote/activitypub/models/image.ts index 9183b82ebe..a6ac698feb 100644 --- a/packages/backend/src/remote/activitypub/models/image.ts +++ b/packages/backend/src/remote/activitypub/models/image.ts @@ -3,7 +3,10 @@ import type { CacheableRemoteUser } from "@/models/entities/user.js"; import Resolver from "../resolver.js"; import { fetchMeta } from "backend-rs"; import { apLogger } from "../logger.js"; -import type { DriveFile, DriveFileUsageHint } from "@/models/entities/drive-file.js"; +import type { + DriveFile, + DriveFileUsageHint, +} from "@/models/entities/drive-file.js"; import { DriveFiles } from "@/models/index.js"; import { truncate } from "@/misc/truncate.js"; import { DB_MAX_IMAGE_COMMENT_LENGTH } from "@/misc/hard-limits.js"; @@ -16,7 +19,7 @@ const logger = apLogger; export async function createImage( actor: CacheableRemoteUser, value: any, - usage: DriveFileUsageHint + usage: DriveFileUsageHint, ): Promise { // Skip if author is frozen. if (actor.isSuspended) { @@ -44,7 +47,7 @@ export async function createImage( sensitive: image.sensitive, isLink: !instance.cacheRemoteFiles, comment: truncate(image.name, DB_MAX_IMAGE_COMMENT_LENGTH), - usageHint: usage + usageHint: usage, }); if (file.isLink) { diff --git a/packages/backend/src/remote/activitypub/models/note.ts b/packages/backend/src/remote/activitypub/models/note.ts index 5e1c3829a7..b2fd67288c 100644 --- a/packages/backend/src/remote/activitypub/models/note.ts +++ b/packages/backend/src/remote/activitypub/models/note.ts @@ -213,7 +213,8 @@ export async function createNote( ? ( await Promise.all( note.attachment.map( - (x) => limit(() => resolveImage(actor, x, null)) as Promise, + (x) => + limit(() => resolveImage(actor, x, null)) as Promise, ), ) ).filter((image) => image != null) diff --git a/packages/backend/src/remote/activitypub/models/person.ts b/packages/backend/src/remote/activitypub/models/person.ts index 5460a0234b..657ac7d553 100644 --- a/packages/backend/src/remote/activitypub/models/person.ts +++ b/packages/backend/src/remote/activitypub/models/person.ts @@ -366,7 +366,11 @@ export async function createPerson( [person.icon, person.image].map((img, index) => img == null ? Promise.resolve(null) - : resolveImage(user, img, index === 0 ? "user_avatar" : index === 1 ? "user_banner" : null).catch(() => null) + : resolveImage( + user, + img, + index === 0 ? "user_avatar" : index === 1 ? "user_banner" : null, + ).catch(() => null), ), ); @@ -442,7 +446,11 @@ export async function updatePerson( [person.icon, person.image].map((img, index) => img == null ? Promise.resolve(null) - : resolveImage(user, img, index === 0 ? "user_avatar" : index === 1 ? "user_banner" : null).catch(() => null), + : resolveImage( + user, + img, + index === 0 ? "user_avatar" : index === 1 ? "user_banner" : null, + ).catch(() => null), ), ); @@ -563,13 +571,13 @@ export async function updatePerson( if (avatar) { if (user?.avatarId) - await DriveFiles.update(user.avatarId, {usageHint: null}); + await DriveFiles.update(user.avatarId, { usageHint: null }); updates.avatarId = avatar.id; } if (banner) { if (user?.bannerId) - await DriveFiles.update(user.bannerId, {usageHint: null}); + await DriveFiles.update(user.bannerId, { usageHint: null }); updates.bannerId = banner.id; } diff --git a/packages/backend/src/server/api/endpoints/i/update.ts b/packages/backend/src/server/api/endpoints/i/update.ts index 9d39903eac..29d03cc465 100644 --- a/packages/backend/src/server/api/endpoints/i/update.ts +++ b/packages/backend/src/server/api/endpoints/i/update.ts @@ -242,7 +242,7 @@ export default define(meta, paramDef, async (ps, _user, token) => { if (ps.emailNotificationTypes !== undefined) profileUpdates.emailNotificationTypes = ps.emailNotificationTypes; - let avatar: DriveFile | null = null + let avatar: DriveFile | null = null; if (ps.avatarId) { avatar = await DriveFiles.findOneBy({ id: ps.avatarId }); @@ -252,7 +252,7 @@ export default define(meta, paramDef, async (ps, _user, token) => { throw new ApiError(meta.errors.avatarNotAnImage); } - let banner: DriveFile | null = null + let banner: DriveFile | null = null; if (ps.bannerId) { banner = await DriveFiles.findOneBy({ id: ps.bannerId }); @@ -332,19 +332,17 @@ export default define(meta, paramDef, async (ps, _user, token) => { //#endregion // Update old/new avatar usage hints - if (avatar) - { + if (avatar) { if (user.avatarId) - await DriveFiles.update(user.avatarId, {usageHint: null}); - await DriveFiles.update(avatar.id, {usageHint: "user_avatar"}); + await DriveFiles.update(user.avatarId, { usageHint: null }); + await DriveFiles.update(avatar.id, { usageHint: "user_avatar" }); } // Update old/new banner usage hints - if (banner) - { + if (banner) { if (user.bannerId) - await DriveFiles.update(user.bannerId, {usageHint: null}); - await DriveFiles.update(banner.id, {usageHint: "user_banner"}); + await DriveFiles.update(user.bannerId, { usageHint: null }); + await DriveFiles.update(banner.id, { usageHint: "user_banner" }); } if (Object.keys(updates).length > 0) await Users.update(user.id, updates); diff --git a/packages/backend/src/services/drive/add-file.ts b/packages/backend/src/services/drive/add-file.ts index 79f30a651b..2f975e1984 100644 --- a/packages/backend/src/services/drive/add-file.ts +++ b/packages/backend/src/services/drive/add-file.ts @@ -75,7 +75,7 @@ async function save( type: string, hash: string, size: number, - usage: DriveFileUsageHint = null + usage: DriveFileUsageHint = null, ): Promise { // thunbnail, webpublic を必要なら生成 const alts = await generateAlts(path, type, !file.uri); @@ -649,7 +649,7 @@ export async function addFile({ info.type.mime, info.md5, info.size, - usageHint + usageHint, ); } diff --git a/packages/backend/src/services/drive/upload-from-url.ts b/packages/backend/src/services/drive/upload-from-url.ts index 238f8714fa..a96e8e3262 100644 --- a/packages/backend/src/services/drive/upload-from-url.ts +++ b/packages/backend/src/services/drive/upload-from-url.ts @@ -3,7 +3,7 @@ import type { User } from "@/models/entities/user.js"; import { createTemp } from "@/misc/create-temp.js"; import { downloadUrl, isPrivateIp } from "@/misc/download-url.js"; import type { DriveFolder } from "@/models/entities/drive-folder.js"; -import type { DriveFile } from "@/models/entities/drive-file.js"; +import type { DriveFile, DriveFileUsageHint } from "@/models/entities/drive-file.js"; import { DriveFiles } from "@/models/index.js"; import { driveLogger } from "./logger.js"; import { addFile } from "./add-file.js"; @@ -26,7 +26,7 @@ type Args = { comment?: string | null; requestIp?: string | null; requestHeaders?: Record | null; - usageHint?: string | null; + usageHint?: DriveFileUsageHint; }; export async function uploadFromUrl({ @@ -40,7 +40,7 @@ export async function uploadFromUrl({ comment = null, requestIp = null, requestHeaders = null, - usageHint = null + usageHint = null, }: Args): Promise { const parsedUrl = new URL(url); if ( @@ -81,7 +81,7 @@ export async function uploadFromUrl({ sensitive, requestIp, requestHeaders, - usageHint + usageHint, }); logger.succ(`Got: ${driveFile.id}`); return driveFile; From 6c46bb56fd2c13672ac6338b970f090f1f3c0672 Mon Sep 17 00:00:00 2001 From: yumeko Date: Fri, 19 Apr 2024 18:22:02 +0300 Subject: [PATCH 07/16] Switch DriveFile's usageHint field to an enum type --- .../src/migration/1713451569342-AddDriveFileUsage.ts | 6 +++++- packages/backend/src/models/entities/drive-file.ts | 7 ++++--- packages/backend/src/remote/activitypub/models/person.ts | 4 ++-- packages/backend/src/server/api/endpoints/i/update.ts | 4 ++-- packages/backend/src/services/drive/add-file.ts | 2 +- 5 files changed, 14 insertions(+), 9 deletions(-) diff --git a/packages/backend/src/migration/1713451569342-AddDriveFileUsage.ts b/packages/backend/src/migration/1713451569342-AddDriveFileUsage.ts index 57c6a73890..3bdb1aafc8 100644 --- a/packages/backend/src/migration/1713451569342-AddDriveFileUsage.ts +++ b/packages/backend/src/migration/1713451569342-AddDriveFileUsage.ts @@ -3,11 +3,15 @@ import type { MigrationInterface, QueryRunner } from "typeorm"; export class AddDriveFileUsage1713451569342 implements MigrationInterface { public async up(queryRunner: QueryRunner): Promise { await queryRunner.query( - `ALTER TABLE "drive_file" ADD "usageHint" character varying(16) DEFAULT NULL`, + `CREATE TYPE drive_file_usage_hint_enum AS ENUM ('userAvatar', 'userBanner')`, + ); + await queryRunner.query( + `ALTER TABLE "drive_file" ADD "usageHint" drive_file_usage_hint_enum DEFAULT NULL`, ); } public async down(queryRunner: QueryRunner): Promise { await queryRunner.query(`ALTER TABLE "drive_file" DROP COLUMN "usageHint"`); + await queryRunner.query(`DROP TYPE drive_file_usage_hint_enum`); } } diff --git a/packages/backend/src/models/entities/drive-file.ts b/packages/backend/src/models/entities/drive-file.ts index f257280d60..2c6c1bf598 100644 --- a/packages/backend/src/models/entities/drive-file.ts +++ b/packages/backend/src/models/entities/drive-file.ts @@ -16,7 +16,7 @@ import { DriveFolder } from "./drive-folder.js"; import { DB_MAX_IMAGE_COMMENT_LENGTH } from "@/misc/hard-limits.js"; import { NoteFile } from "./note-file.js"; -export type DriveFileUsageHint = "user_avatar" | "user_banner" | null; +export type DriveFileUsageHint = "userAvatar" | "userBanner" | null; @Entity() @Index(["userId", "folderId", "id"]) @@ -179,8 +179,9 @@ export class DriveFile { }) public isSensitive: boolean; - @Column("varchar", { - length: 16, + @Column({ + type: "enum", + enum: ["userAvatar", "userBanner"], nullable: true, comment: "Hint for what the file is used for.", }) diff --git a/packages/backend/src/remote/activitypub/models/person.ts b/packages/backend/src/remote/activitypub/models/person.ts index 657ac7d553..4baa2c021b 100644 --- a/packages/backend/src/remote/activitypub/models/person.ts +++ b/packages/backend/src/remote/activitypub/models/person.ts @@ -369,7 +369,7 @@ export async function createPerson( : resolveImage( user, img, - index === 0 ? "user_avatar" : index === 1 ? "user_banner" : null, + index === 0 ? "userAvatar" : index === 1 ? "userBanner" : null, ).catch(() => null), ), ); @@ -449,7 +449,7 @@ export async function updatePerson( : resolveImage( user, img, - index === 0 ? "user_avatar" : index === 1 ? "user_banner" : null, + index === 0 ? "userAvatar" : index === 1 ? "userBanner" : null, ).catch(() => null), ), ); diff --git a/packages/backend/src/server/api/endpoints/i/update.ts b/packages/backend/src/server/api/endpoints/i/update.ts index 29d03cc465..4f65c59a9e 100644 --- a/packages/backend/src/server/api/endpoints/i/update.ts +++ b/packages/backend/src/server/api/endpoints/i/update.ts @@ -335,14 +335,14 @@ export default define(meta, paramDef, async (ps, _user, token) => { if (avatar) { if (user.avatarId) await DriveFiles.update(user.avatarId, { usageHint: null }); - await DriveFiles.update(avatar.id, { usageHint: "user_avatar" }); + await DriveFiles.update(avatar.id, { usageHint: "userAvatar" }); } // Update old/new banner usage hints if (banner) { if (user.bannerId) await DriveFiles.update(user.bannerId, { usageHint: null }); - await DriveFiles.update(banner.id, { usageHint: "user_banner" }); + await DriveFiles.update(banner.id, { usageHint: "userBanner" }); } if (Object.keys(updates).length > 0) await Users.update(user.id, updates); diff --git a/packages/backend/src/services/drive/add-file.ts b/packages/backend/src/services/drive/add-file.ts index 2f975e1984..d180bbabf3 100644 --- a/packages/backend/src/services/drive/add-file.ts +++ b/packages/backend/src/services/drive/add-file.ts @@ -66,7 +66,7 @@ function urlPathJoin( * @param type Content-Type for original * @param hash Hash for original * @param size Size for original - * @param usage Optional usage hint for file (f.e. "user_avatar") + * @param usage Optional usage hint for file (f.e. "userAvatar") */ async function save( file: DriveFile, From 43570a54aa3e146698487d5ee39686f428b91397 Mon Sep 17 00:00:00 2001 From: naskya Date: Sun, 21 Apr 2024 10:44:54 +0900 Subject: [PATCH 08/16] chore: format --- packages/backend/src/services/drive/upload-from-url.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/backend/src/services/drive/upload-from-url.ts b/packages/backend/src/services/drive/upload-from-url.ts index a96e8e3262..e7b084bda1 100644 --- a/packages/backend/src/services/drive/upload-from-url.ts +++ b/packages/backend/src/services/drive/upload-from-url.ts @@ -3,7 +3,10 @@ import type { User } from "@/models/entities/user.js"; import { createTemp } from "@/misc/create-temp.js"; import { downloadUrl, isPrivateIp } from "@/misc/download-url.js"; import type { DriveFolder } from "@/models/entities/drive-folder.js"; -import type { DriveFile, DriveFileUsageHint } from "@/models/entities/drive-file.js"; +import type { + DriveFile, + DriveFileUsageHint, +} from "@/models/entities/drive-file.js"; import { DriveFiles } from "@/models/index.js"; import { driveLogger } from "./logger.js"; import { addFile } from "./add-file.js"; From c936102a4c95b4b6e12b3caf46ef9bcdde4f4e2c Mon Sep 17 00:00:00 2001 From: naskya Date: Sun, 21 Apr 2024 10:45:47 +0900 Subject: [PATCH 09/16] chore (backend-rs): regenerate entities and index.js/d.ts --- packages/backend-rs/index.d.ts | 5 +++++ packages/backend-rs/index.js | 3 ++- packages/backend-rs/src/model/entity/drive_file.rs | 3 ++- .../src/model/entity/sea_orm_active_enums.rs | 14 ++++++++++++++ 4 files changed, 23 insertions(+), 2 deletions(-) diff --git a/packages/backend-rs/index.d.ts b/packages/backend-rs/index.d.ts index 1cf961bd30..5a7bf218c5 100644 --- a/packages/backend-rs/index.d.ts +++ b/packages/backend-rs/index.d.ts @@ -348,6 +348,7 @@ export interface DriveFile { webpublicType: string | null requestHeaders: Json | null requestIp: string | null + usageHint: DriveFileUsageHintEnum | null } export interface DriveFolder { id: string @@ -780,6 +781,10 @@ export enum AntennaSrcEnum { List = 'list', Users = 'users' } +export enum DriveFileUsageHintEnum { + UserAvatar = 'userAvatar', + UserBanner = 'userBanner' +} export enum MutedNoteReasonEnum { Manual = 'manual', Other = 'other', diff --git a/packages/backend-rs/index.js b/packages/backend-rs/index.js index 1ea7bb5bed..6d64f6ec75 100644 --- a/packages/backend-rs/index.js +++ b/packages/backend-rs/index.js @@ -310,7 +310,7 @@ if (!nativeBinding) { throw new Error(`Failed to load native binding`) } -const { readEnvironmentConfig, readServerConfig, stringToAcct, acctToString, checkWordMute, getFullApAccount, isSelfHost, isSameOrigin, extractHost, toPuny, isUnicodeEmoji, sqlLikeEscape, safeForSql, formatMilliseconds, getNoteSummary, toMastodonId, fromMastodonId, fetchMeta, metaToPugArgs, nyaify, hashPassword, verifyPassword, isOldPasswordAlgorithm, decodeReaction, countReactions, toDbReaction, AntennaSrcEnum, MutedNoteReasonEnum, NoteVisibilityEnum, NotificationTypeEnum, PageVisibilityEnum, PollNotevisibilityEnum, RelayStatusEnum, UserEmojimodpermEnum, UserProfileFfvisibilityEnum, UserProfileMutingnotificationtypesEnum, initIdGenerator, getTimestamp, genId, secureRndstr } = nativeBinding +const { readEnvironmentConfig, readServerConfig, stringToAcct, acctToString, checkWordMute, getFullApAccount, isSelfHost, isSameOrigin, extractHost, toPuny, isUnicodeEmoji, sqlLikeEscape, safeForSql, formatMilliseconds, getNoteSummary, toMastodonId, fromMastodonId, fetchMeta, metaToPugArgs, nyaify, hashPassword, verifyPassword, isOldPasswordAlgorithm, decodeReaction, countReactions, toDbReaction, AntennaSrcEnum, DriveFileUsageHintEnum, MutedNoteReasonEnum, NoteVisibilityEnum, NotificationTypeEnum, PageVisibilityEnum, PollNotevisibilityEnum, RelayStatusEnum, UserEmojimodpermEnum, UserProfileFfvisibilityEnum, UserProfileMutingnotificationtypesEnum, initIdGenerator, getTimestamp, genId, secureRndstr } = nativeBinding module.exports.readEnvironmentConfig = readEnvironmentConfig module.exports.readServerConfig = readServerConfig @@ -339,6 +339,7 @@ module.exports.decodeReaction = decodeReaction module.exports.countReactions = countReactions module.exports.toDbReaction = toDbReaction module.exports.AntennaSrcEnum = AntennaSrcEnum +module.exports.DriveFileUsageHintEnum = DriveFileUsageHintEnum module.exports.MutedNoteReasonEnum = MutedNoteReasonEnum module.exports.NoteVisibilityEnum = NoteVisibilityEnum module.exports.NotificationTypeEnum = NotificationTypeEnum diff --git a/packages/backend-rs/src/model/entity/drive_file.rs b/packages/backend-rs/src/model/entity/drive_file.rs index e5c3995573..a6926e7af2 100644 --- a/packages/backend-rs/src/model/entity/drive_file.rs +++ b/packages/backend-rs/src/model/entity/drive_file.rs @@ -1,5 +1,6 @@ //! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15 +use super::sea_orm_active_enums::DriveFileUsageHintEnum; use sea_orm::entity::prelude::*; #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)] @@ -53,7 +54,7 @@ pub struct Model { #[sea_orm(column_name = "requestIp")] pub request_ip: Option, #[sea_orm(column_name = "usageHint")] - pub usage_hint: Option, + pub usage_hint: Option, } #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] diff --git a/packages/backend-rs/src/model/entity/sea_orm_active_enums.rs b/packages/backend-rs/src/model/entity/sea_orm_active_enums.rs index 38820e1bd8..36281f4dc5 100644 --- a/packages/backend-rs/src/model/entity/sea_orm_active_enums.rs +++ b/packages/backend-rs/src/model/entity/sea_orm_active_enums.rs @@ -23,6 +23,20 @@ pub enum AntennaSrcEnum { #[derive(Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum)] #[cfg_attr(not(feature = "napi"), derive(Clone))] #[cfg_attr(feature = "napi", napi_derive::napi(string_enum = "camelCase"))] +#[sea_orm( + rs_type = "String", + db_type = "Enum", + enum_name = "drive_file_usage_hint_enum" +)] +pub enum DriveFileUsageHintEnum { + #[sea_orm(string_value = "userAvatar")] + UserAvatar, + #[sea_orm(string_value = "userBanner")] + UserBanner, +} +#[derive(Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum)] +#[cfg_attr(not(feature = "napi"), derive(Clone))] +#[cfg_attr(feature = "napi", napi_derive::napi(string_enum = "camelCase"))] #[sea_orm( rs_type = "String", db_type = "Enum", From 96481f1353dd665b0c05d776d5df54f6ce2a57a8 Mon Sep 17 00:00:00 2001 From: naskya Date: Sun, 21 Apr 2024 10:48:31 +0900 Subject: [PATCH 10/16] chore: update downgrade.sql --- docs/downgrade.sql | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/docs/downgrade.sql b/docs/downgrade.sql index 77dea27573..44222f818f 100644 --- a/docs/downgrade.sql +++ b/docs/downgrade.sql @@ -1,6 +1,7 @@ BEGIN; DELETE FROM "migrations" WHERE name IN ( + 'AddDriveFileUsage1713451569342', 'ConvertCwVarcharToText1713225866247', 'FixChatFileConstraint1712855579316', 'DropTimeZone1712425488543', @@ -23,7 +24,11 @@ DELETE FROM "migrations" WHERE name IN ( 'RemoveNativeUtilsMigration1705877093218' ); ---convert-cw-varchar-to-text +-- AddDriveFileUsage +ALTER TABLE "drive_file" DROP COLUMN "usageHint"; +DROP TYPE "drive_file_usage_hint_enum"; + +-- convert-cw-varchar-to-text DROP INDEX "IDX_8e3bbbeb3df04d1a8105da4c8f"; ALTER TABLE "note" ALTER COLUMN "cw" TYPE character varying(512); CREATE INDEX "IDX_8e3bbbeb3df04d1a8105da4c8f" ON "note" USING "pgroonga" ("cw" pgroonga_varchar_full_text_search_ops_v2); From d2dbfb37c72234e4b4a3b23867c48ec9034861b3 Mon Sep 17 00:00:00 2001 From: naskya Date: Sun, 21 Apr 2024 10:59:02 +0900 Subject: [PATCH 11/16] chore (backend): reflect entity changes to the schema and repository --- packages/backend/src/models/repositories/drive-file.ts | 2 ++ packages/backend/src/models/schema/drive-file.ts | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/packages/backend/src/models/repositories/drive-file.ts b/packages/backend/src/models/repositories/drive-file.ts index 2321f20d4c..18b139caff 100644 --- a/packages/backend/src/models/repositories/drive-file.ts +++ b/packages/backend/src/models/repositories/drive-file.ts @@ -152,6 +152,7 @@ export const DriveFileRepository = db.getRepository(DriveFile).extend({ md5: file.md5, size: file.size, isSensitive: file.isSensitive, + usageHint: file.usageHint, blurhash: file.blurhash, properties: opts.self ? file.properties : this.getPublicProperties(file), url: opts.self ? file.url : this.getPublicUrl(file, false), @@ -193,6 +194,7 @@ export const DriveFileRepository = db.getRepository(DriveFile).extend({ md5: file.md5, size: file.size, isSensitive: file.isSensitive, + usageHint: file.usageHint, blurhash: file.blurhash, properties: opts.self ? file.properties : this.getPublicProperties(file), url: opts.self ? file.url : this.getPublicUrl(file, false), diff --git a/packages/backend/src/models/schema/drive-file.ts b/packages/backend/src/models/schema/drive-file.ts index 30db9e7d48..929dbb472e 100644 --- a/packages/backend/src/models/schema/drive-file.ts +++ b/packages/backend/src/models/schema/drive-file.ts @@ -44,6 +44,12 @@ export const packedDriveFileSchema = { optional: false, nullable: false, }, + usageHint: { + type: "string", + optional: false, + nullable: true, + enum: ["userAvatar", "userBanner"], + }, blurhash: { type: "string", optional: false, From 6b008c651a899a47b202f1d25b6c5ed56577e962 Mon Sep 17 00:00:00 2001 From: naskya Date: Sun, 21 Apr 2024 11:09:18 +0900 Subject: [PATCH 12/16] chore (backend): remove (technically) incorrect TypeORM decorator field --- packages/backend/src/models/entities/drive-file.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/backend/src/models/entities/drive-file.ts b/packages/backend/src/models/entities/drive-file.ts index 2c6c1bf598..81f564115f 100644 --- a/packages/backend/src/models/entities/drive-file.ts +++ b/packages/backend/src/models/entities/drive-file.ts @@ -179,11 +179,11 @@ export class DriveFile { }) public isSensitive: boolean; + // Hint for what this file is used for @Column({ type: "enum", enum: ["userAvatar", "userBanner"], nullable: true, - comment: "Hint for what the file is used for.", }) public usageHint: DriveFileUsageHint; From ce672f4edd542d78a1b33429d379ec969ae163cb Mon Sep 17 00:00:00 2001 From: naskya Date: Sun, 21 Apr 2024 22:36:05 +0900 Subject: [PATCH 13/16] dev: add cargo test to pnpm scripts mocha test has been unmaintained for a long time and is very broken :( --- package.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index a62fae09f0..93594a5698 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,9 @@ "debug": "pnpm run build:debug && pnpm run start", "build:debug": "pnpm run clean && pnpm node ./scripts/dev-build.mjs && pnpm run gulp", "mocha": "pnpm --filter backend run mocha", - "test": "pnpm run mocha", + "test": "pnpm run test:ts && pnpm run test:rs", + "test:ts": "pnpm run mocha", + "test:rs": "cargo test", "format": "pnpm run format:ts; pnpm run format:rs", "format:ts": "pnpm -r --parallel run format", "format:rs": "cargo fmt --all --", From a107d8c1ec8309ca9d76583acdcf2d072fda3bfc Mon Sep 17 00:00:00 2001 From: naskya Date: Mon, 22 Apr 2024 05:52:56 +0900 Subject: [PATCH 14/16] fix (backend): update import --- packages/backend/src/server/api/endpoints/antennas/create.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/backend/src/server/api/endpoints/antennas/create.ts b/packages/backend/src/server/api/endpoints/antennas/create.ts index 792c5e30b4..aa5dcee044 100644 --- a/packages/backend/src/server/api/endpoints/antennas/create.ts +++ b/packages/backend/src/server/api/endpoints/antennas/create.ts @@ -1,9 +1,8 @@ import define from "@/server/api/define.js"; -import { genId } from "backend-rs"; +import { fetchMeta, genId } from "backend-rs"; import { Antennas, UserLists, UserGroupJoinings } from "@/models/index.js"; import { ApiError } from "@/server/api/error.js"; import { publishInternalEvent } from "@/services/stream.js"; -import { fetchMeta } from "@/misc/fetch-meta.js"; export const meta = { tags: ["antennas"], From c4658801aa98e61f5ee14177583cc758c1904f1d Mon Sep 17 00:00:00 2001 From: naskya Date: Mon, 22 Apr 2024 05:54:32 +0900 Subject: [PATCH 15/16] chore: regenerate entities --- packages/backend-rs/src/model/entity/meta.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/backend-rs/src/model/entity/meta.rs b/packages/backend-rs/src/model/entity/meta.rs index 7d0972b844..fcba9e0be9 100644 --- a/packages/backend-rs/src/model/entity/meta.rs +++ b/packages/backend-rs/src/model/entity/meta.rs @@ -50,8 +50,6 @@ pub struct Model { pub local_drive_capacity_mb: i32, #[sea_orm(column_name = "remoteDriveCapacityMb")] pub remote_drive_capacity_mb: i32, - #[sea_orm(column_name = "antennaLimit")] - pub antenna_limit: i32, #[sea_orm(column_name = "summalyProxy")] pub summaly_proxy: Option, #[sea_orm(column_name = "enableEmail")] @@ -175,6 +173,8 @@ pub struct Model { pub more_urls: Json, #[sea_orm(column_name = "markLocalFilesNsfwByDefault")] pub mark_local_files_nsfw_by_default: bool, + #[sea_orm(column_name = "antennaLimit")] + pub antenna_limit: i32, } #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] From c9de5f6095dec1c3390a4e5c0300dd0fa296620a Mon Sep 17 00:00:00 2001 From: naskya Date: Mon, 22 Apr 2024 05:56:46 +0900 Subject: [PATCH 16/16] docs: update api-changes.md --- docs/api-change.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/api-change.md b/docs/api-change.md index f3ed584c32..dcd4329a27 100644 --- a/docs/api-change.md +++ b/docs/api-change.md @@ -2,6 +2,10 @@ Breaking changes are indicated by the :warning: icon. +## Unreleased + +- Added `antennaLimit` field to the response of `meta` and `admin/meta`, and the request of `admin/update-meta` (optional). + ## v20240413 - :warning: Removed `patrons` endpoint.