mirror of
https://example.com
synced 2024-11-22 08:56:39 +09:00
refactor (backend): add note_file table to store (noteId, fileId)
This commit is contained in:
parent
a12ac93a41
commit
bc5f233b9a
18 changed files with 161 additions and 36 deletions
|
@ -35,6 +35,7 @@ pub mod muting;
|
|||
pub mod note;
|
||||
pub mod note_edit;
|
||||
pub mod note_favorite;
|
||||
pub mod note_file;
|
||||
pub mod note_reaction;
|
||||
pub mod note_thread_muting;
|
||||
pub mod note_unread;
|
||||
|
|
20
packages/backend-rs/src/model/entity/note_file.rs
Normal file
20
packages/backend-rs/src/model/entity/note_file.rs
Normal file
|
@ -0,0 +1,20 @@
|
|||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.12
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
|
||||
#[sea_orm(table_name = "note_file")]
|
||||
#[napi_derive::napi(object, js_name = "NoteFile", use_nullable = true)]
|
||||
pub struct Model {
|
||||
#[sea_orm(column_name = "idNum", primary_key)]
|
||||
pub id_num: i64,
|
||||
#[sea_orm(column_name = "noteId")]
|
||||
pub note_id: String,
|
||||
#[sea_orm(column_name = "fileId")]
|
||||
pub file_id: String,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||
pub enum Relation {}
|
||||
|
||||
impl ActiveModelBehavior for ActiveModel {}
|
|
@ -33,6 +33,7 @@ pub use super::muting::Entity as Muting;
|
|||
pub use super::note::Entity as Note;
|
||||
pub use super::note_edit::Entity as NoteEdit;
|
||||
pub use super::note_favorite::Entity as NoteFavorite;
|
||||
pub use super::note_file::Entity as NoteFile;
|
||||
pub use super::note_reaction::Entity as NoteReaction;
|
||||
pub use super::note_thread_muting::Entity as NoteThreadMuting;
|
||||
pub use super::note_unread::Entity as NoteUnread;
|
||||
|
|
|
@ -145,8 +145,8 @@ pub enum UserEmojimodpermEnum {
|
|||
Full,
|
||||
#[sea_orm(string_value = "mod")]
|
||||
Mod,
|
||||
#[sea_orm(string_value = "unauthorized")]
|
||||
Unauthorized,
|
||||
#[sea_orm(string_value = "none")]
|
||||
None,
|
||||
}
|
||||
#[derive(Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum)]
|
||||
#[napi_derive::napi]
|
||||
|
|
|
@ -60,7 +60,6 @@ pub struct Model {
|
|||
pub receive_announcement_email: bool,
|
||||
#[sea_orm(column_name = "emailNotificationTypes", column_type = "JsonBinary")]
|
||||
pub email_notification_types: Json,
|
||||
pub lang: Option<String>,
|
||||
#[sea_orm(column_name = "mutedInstances", column_type = "JsonBinary")]
|
||||
pub muted_instances: Json,
|
||||
#[sea_orm(column_name = "publicReactions")]
|
||||
|
|
|
@ -73,6 +73,7 @@ import { UserPending } from "@/models/entities/user-pending.js";
|
|||
import { Webhook } from "@/models/entities/webhook.js";
|
||||
import { UserIp } from "@/models/entities/user-ip.js";
|
||||
import { NoteEdit } from "@/models/entities/note-edit.js";
|
||||
import { NoteFile } from "@/models/entities/note-file.js";
|
||||
|
||||
import { entities as charts } from "@/services/chart/entities.js";
|
||||
import { dbLogger } from "./logger.js";
|
||||
|
@ -143,6 +144,7 @@ export const entities = [
|
|||
Note,
|
||||
NoteEdit,
|
||||
NoteFavorite,
|
||||
NoteFile,
|
||||
NoteReaction,
|
||||
NoteWatching,
|
||||
NoteThreadMuting,
|
||||
|
|
|
@ -13,22 +13,9 @@ export class Pgroonga1698420787202 implements MigrationInterface {
|
|||
);
|
||||
}
|
||||
|
||||
<<<<<<<< HEAD:packages/backend/migration-neko/1698420787202-pgroonga.js
|
||||
async down(queryRunner) {
|
||||
await queryRunner.query(
|
||||
`DROP INDEX "public"."IDX_fcb770976ff8240af5799e3ffc"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`DROP INDEX "public"."IDX_065d4d8f3b5adb4a08841eae3c"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`DROP INDEX "public"."IDX_f27f5d88941e57442be75ba9c8"`,
|
||||
);
|
||||
========
|
||||
async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`DROP INDEX "IDX_fcb770976ff8240af5799e3ffc"`);
|
||||
await queryRunner.query(`DROP INDEX "IDX_065d4d8f3b5adb4a08841eae3c"`);
|
||||
await queryRunner.query(`DROP INDEX "IDX_f27f5d88941e57442be75ba9c8"`);
|
||||
>>>>>>>> bf9916740 (refactor (backend): move migrations inside backend/src):packages/backend/src/migration/1698420787202-pgroonga.ts
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,15 +0,0 @@
|
|||
import { MigrationInterface, QueryRunner } from "typeorm";
|
||||
|
||||
export class DropUserProfileLanguage1708452631156
|
||||
implements MigrationInterface
|
||||
{
|
||||
async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`ALTER TABLE "user_profile" DROP COLUMN "lang"`);
|
||||
}
|
||||
|
||||
async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "user_profile" ADD COLUMN "lang" character varying(32)`,
|
||||
);
|
||||
}
|
||||
}
|
32
packages/backend/src/migration/1710304584214-note-file.ts
Normal file
32
packages/backend/src/migration/1710304584214-note-file.ts
Normal file
|
@ -0,0 +1,32 @@
|
|||
import { MigrationInterface, QueryRunner, TableIndex } from "typeorm";
|
||||
|
||||
export class NoteFile1710304584214 implements MigrationInterface {
|
||||
name = "NoteFile1710304584214";
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`CREATE TABLE "note_file" ("idNum" bigserial PRIMARY KEY, "noteId" varchar(32) NOT NULL, "fileId" varchar(32) NOT NULL)`,
|
||||
);
|
||||
await queryRunner.query(`
|
||||
INSERT INTO "note_file" ("noteId", "fileId") SELECT * FROM (
|
||||
SELECT "id", UNNEST("fileIds") FROM "note"
|
||||
)
|
||||
`);
|
||||
await queryRunner.createIndices("note_file", [
|
||||
{
|
||||
name: "IDX_note_file_noteId",
|
||||
columnNames: ["noteId"],
|
||||
isUnique: false,
|
||||
},
|
||||
{
|
||||
name: "IDX_note_file_fileId",
|
||||
columnNames: ["fileId"],
|
||||
isUnique: false,
|
||||
},
|
||||
] as TableIndex[]);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.dropTable("note_file");
|
||||
}
|
||||
}
|
|
@ -32,6 +32,7 @@ import { packedQueueCountSchema } from "@/models/schema/queue.js";
|
|||
import { packedGalleryPostSchema } from "@/models/schema/gallery-post.js";
|
||||
import { packedEmojiSchema } from "@/models/schema/emoji.js";
|
||||
import { packedNoteEdit } from "@/models/schema/note-edit.js";
|
||||
import { packedNoteFileSchema } from "@/models/schema/note-file.js";
|
||||
|
||||
export const refs = {
|
||||
UserLite: packedUserLiteSchema,
|
||||
|
@ -47,6 +48,7 @@ export const refs = {
|
|||
App: packedAppSchema,
|
||||
MessagingMessage: packedMessagingMessageSchema,
|
||||
Note: packedNoteSchema,
|
||||
NoteFile: packedNoteFileSchema,
|
||||
NoteEdit: packedNoteEdit,
|
||||
NoteReaction: packedNoteReactionSchema,
|
||||
NoteFavorite: packedNoteFavoriteSchema,
|
||||
|
|
25
packages/backend/src/models/entities/note-file.ts
Normal file
25
packages/backend/src/models/entities/note-file.ts
Normal file
|
@ -0,0 +1,25 @@
|
|||
import { Entity, Index, Column, PrimaryGeneratedColumn } from "typeorm";
|
||||
import { Note } from "./note.js";
|
||||
import { DriveFile } from "./drive-file.js";
|
||||
import { id } from "../id.js";
|
||||
|
||||
@Entity()
|
||||
export class NoteFile {
|
||||
// We don't use this column but TypeORM requires a primary column
|
||||
@PrimaryGeneratedColumn("increment")
|
||||
public idNum: number;
|
||||
|
||||
@Index("IDX_note_file_noteId", { unique: false })
|
||||
@Column({
|
||||
...id(),
|
||||
nullable: false,
|
||||
})
|
||||
public noteId: Note["id"];
|
||||
|
||||
@Index("IDX_note_file_fileId", { unique: false })
|
||||
@Column({
|
||||
...id(),
|
||||
nullable: false,
|
||||
})
|
||||
public fileId: DriveFile["id"];
|
||||
}
|
|
@ -151,6 +151,7 @@ export class Note {
|
|||
})
|
||||
public score: number;
|
||||
|
||||
// TODO: drop this column and use note_files
|
||||
@Index()
|
||||
@Column({
|
||||
...id(),
|
||||
|
|
|
@ -66,12 +66,14 @@ import { InstanceRepository } from "./repositories/instance.js";
|
|||
import { Webhook } from "./entities/webhook.js";
|
||||
import { UserIp } from "./entities/user-ip.js";
|
||||
import { NoteEdit } from "./entities/note-edit.js";
|
||||
import { NoteFileRepository } from "./repositories/note-file.js";
|
||||
|
||||
export const Announcements = db.getRepository(Announcement);
|
||||
export const AnnouncementReads = db.getRepository(AnnouncementRead);
|
||||
export const Apps = AppRepository;
|
||||
export const Notes = NoteRepository;
|
||||
export const NoteEdits = db.getRepository(NoteEdit);
|
||||
export const NoteFiles = NoteFileRepository;
|
||||
export const NoteFavorites = NoteFavoriteRepository;
|
||||
export const NoteWatchings = db.getRepository(NoteWatching);
|
||||
export const NoteThreadMutings = db.getRepository(NoteThreadMuting);
|
||||
|
|
4
packages/backend/src/models/repositories/note-file.ts
Normal file
4
packages/backend/src/models/repositories/note-file.ts
Normal file
|
@ -0,0 +1,4 @@
|
|||
import { db } from "@/db/postgre.js";
|
||||
import { NoteFile } from "@/models/entities/note-file.js";
|
||||
|
||||
export const NoteFileRepository = db.getRepository(NoteFile).extend({});
|
19
packages/backend/src/models/schema/note-file.ts
Normal file
19
packages/backend/src/models/schema/note-file.ts
Normal file
|
@ -0,0 +1,19 @@
|
|||
export const packedNoteFileSchema = {
|
||||
type: "object",
|
||||
properties: {
|
||||
noteId: {
|
||||
type: "string",
|
||||
optional: false,
|
||||
nullable: false,
|
||||
format: "id",
|
||||
example: "xxxxxxxxxx",
|
||||
},
|
||||
fileId: {
|
||||
type: "string",
|
||||
optional: false,
|
||||
nullable: false,
|
||||
format: "id",
|
||||
example: "xxxxxxxxxx",
|
||||
},
|
||||
},
|
||||
} as const;
|
|
@ -1,10 +1,14 @@
|
|||
import type { DriveFile } from "@/models/entities/drive-file.js";
|
||||
import { InternalStorage } from "./internal-storage.js";
|
||||
import { DriveFiles } from "@/models/index.js";
|
||||
import { DriveFiles, NoteFiles } from "@/models/index.js";
|
||||
import { createDeleteObjectStorageFileJob } from "@/queue/index.js";
|
||||
import { fetchMeta } from "@/misc/backend-rs.js";
|
||||
import { getS3 } from "./s3.js";
|
||||
import { v4 as uuid } from "uuid";
|
||||
import Logger from "@/services/logger.js";
|
||||
import { inspect } from "node:util";
|
||||
|
||||
const logger = new Logger("delete-file");
|
||||
|
||||
export async function deleteFile(file: DriveFile, isExpired = false) {
|
||||
if (file.storedInternal) {
|
||||
|
@ -79,6 +83,15 @@ async function postProcess(file: DriveFile, isExpired = false) {
|
|||
} else {
|
||||
DriveFiles.delete(file.id);
|
||||
}
|
||||
|
||||
const noteFiles = await NoteFiles.findBy({ fileId: file.id });
|
||||
await Promise.all(
|
||||
noteFiles.map(async (noteFile) => {
|
||||
await NoteFiles.delete(noteFile);
|
||||
}),
|
||||
).catch((e) => {
|
||||
logger.error(inspect(e));
|
||||
});
|
||||
}
|
||||
|
||||
export async function deleteObjectStorageFile(key: string) {
|
||||
|
|
|
@ -33,6 +33,7 @@ import {
|
|||
Channels,
|
||||
ChannelFollowings,
|
||||
NoteThreadMutings,
|
||||
NoteFiles,
|
||||
} from "@/models/index.js";
|
||||
import type { DriveFile } from "@/models/entities/drive-file.js";
|
||||
import type { App } from "@/models/entities/app.js";
|
||||
|
@ -342,6 +343,14 @@ export default async (
|
|||
|
||||
const note = await insertNote(user, data, tags, emojis, mentionedUsers);
|
||||
|
||||
await Promise.all(
|
||||
note.fileIds.map(async (fileId) => {
|
||||
await NoteFiles.insert({ noteId: note.id, fileId });
|
||||
}),
|
||||
).catch((e) => {
|
||||
logger.error(inspect(e));
|
||||
});
|
||||
|
||||
res(note);
|
||||
|
||||
// Register host
|
||||
|
|
|
@ -8,7 +8,7 @@ import renderTombstone from "@/remote/activitypub/renderer/tombstone.js";
|
|||
import config from "@/config/index.js";
|
||||
import type { User, ILocalUser, IRemoteUser } from "@/models/entities/user.js";
|
||||
import type { Note, IMentionedRemoteUsers } from "@/models/entities/note.js";
|
||||
import { Notes, Users, Instances } from "@/models/index.js";
|
||||
import { Notes, NoteFiles, Users, Instances } from "@/models/index.js";
|
||||
import {
|
||||
deliverToFollowers,
|
||||
deliverToUser,
|
||||
|
@ -16,7 +16,10 @@ import {
|
|||
import { hasOtherRenoteOfThisNote } from "@/misc/backend-rs.js";
|
||||
import { registerOrFetchInstanceDoc } from "@/services/register-or-fetch-instance-doc.js";
|
||||
import { deliverToRelays } from "@/services/relay.js";
|
||||
// import meilisearch from "@/db/meilisearch.js";
|
||||
import Logger from "@/services/logger.js";
|
||||
import { inspect } from "node:util";
|
||||
|
||||
const logger = new Logger("delete-note");
|
||||
|
||||
/**
|
||||
* 投稿を削除します。
|
||||
|
@ -87,11 +90,23 @@ export default async function (
|
|||
deliverToConcerned(user, note, content);
|
||||
}
|
||||
|
||||
const cascadingNotes = await findCascadingNotes(note);
|
||||
|
||||
for await (const cascadingNote of cascadingNotes) {
|
||||
await Promise.all(
|
||||
cascadingNote.fileIds.map(async (fileId) => {
|
||||
await NoteFiles.delete({ noteId: cascadingNote.id, fileId });
|
||||
}),
|
||||
).catch((e) => {
|
||||
logger.error(inspect(e));
|
||||
});
|
||||
}
|
||||
|
||||
// also deliever delete activity to cascaded notes
|
||||
const cascadingNotes = (await findCascadingNotes(note)).filter(
|
||||
const cascadingFederatedNotes = cascadingNotes.filter(
|
||||
(note) => !note.localOnly,
|
||||
); // filter out local-only notes
|
||||
for (const cascadingNote of cascadingNotes) {
|
||||
for (const cascadingNote of cascadingFederatedNotes) {
|
||||
if (!cascadingNote.user) continue;
|
||||
if (!Users.isLocalUser(cascadingNote.user)) continue;
|
||||
const content = renderActivity(
|
||||
|
@ -116,6 +131,14 @@ export default async function (
|
|||
id: note.id,
|
||||
userId: user.id,
|
||||
});
|
||||
|
||||
await Promise.all(
|
||||
note.fileIds.map(async (fileId) => {
|
||||
await NoteFiles.delete({ noteId: note.id, fileId });
|
||||
}),
|
||||
).catch((e) => {
|
||||
logger.error(inspect(e));
|
||||
});
|
||||
}
|
||||
|
||||
// if (meilisearch) {
|
||||
|
|
Loading…
Reference in a new issue