From de4da3c1fd491376e4b59585e28d339e26722989 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=80=81=E5=91=A8=E9=83=A8=E8=90=BD?= Date: Tue, 19 Mar 2024 22:37:46 +0800 Subject: [PATCH 001/269] docs: add minimum dependencies --- dev/docs/local-installation.md | 2 ++ docs/install.md | 30 ++++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/dev/docs/local-installation.md b/dev/docs/local-installation.md index a13aa6437f..132d86bd56 100644 --- a/dev/docs/local-installation.md +++ b/dev/docs/local-installation.md @@ -42,6 +42,8 @@ cargo --version ### PostgreSQL and PGroonga +Firefish requires PostgreSQL v12 or later. While you can choose any versions between v12 and the latest version (v16.2 as of writing), we recommend that you install v12.x so as not to use new features inadvertently and introduce incompatibility issues. + PostgreSQL install instructions can be found at [this page](https://www.postgresql.org/download/). ```sh diff --git a/docs/install.md b/docs/install.md index 061000fa32..f030e87125 100644 --- a/docs/install.md +++ b/docs/install.md @@ -4,6 +4,8 @@ This document shows an example procedure for installing Firefish on Debian 12. N If you want to use the pre-built container image, please refer to [`install-container.md`](./install-container.md). +If you do not prepare your environment as document, be sure to meet the minimum dependencies given at the bottom of the page. + Make sure that you can use the `sudo` command before proceeding. ## 1. Install dependencies @@ -315,3 +317,31 @@ cd ~/firefish - Run `psql -d firefish` (or whatever the database name is) - Run `UPDATE "user" SET "isAdmin" = true WHERE id='999999';` (replace `999999` with the copied ID) - Restart your Firefish server + +## Dependencies + +**We only recommend that you use components that are still within the upstream support cycle for better performance and security, and it is recommended that new sites meet the recommended dependency version requirements.** + +- At least [NodeJS](https://nodejs.org/en/) v18.17.0 (v20/v21 recommended) +- At least [PostgreSQL](https://www.postgresql.org/) v12 (v16 recommended) with [PGroonga](https://pgroonga.github.io/) extension +- At least [Redis](https://redis.io/) v7 +- Web Proxy (one of the following) + - Caddy (recommended for new users) + - Nginx (recommended) + - Apache + +### Optional dependencies + +- [FFmpeg](https://ffmpeg.org/) for video transcoding +- Caching server (one of the following) + - [DragonflyDB](https://www.dragonflydb.io/) (recommended) + - [KeyDB](https://keydb.dev/) + - Another [Redis](https://redis.io/) server + +### Build dependencies + +- At least [Rust](https://www.rust-lang.org/) v1.74 +- C/C++ compiler & build tools + - `build-essential` on Debian/Ubuntu Linux + - `base-devel` on Arch Linux +- [Python 3](https://www.python.org/) From d64d133d7f9d741a16d374c200710787f6b04b8e Mon Sep 17 00:00:00 2001 From: Lhcfl Date: Tue, 26 Mar 2024 01:33:42 +0800 Subject: [PATCH 002/269] backend: added /note/history endpoint --- packages/backend/src/models/index.ts | 4 +- .../src/models/repositories/note-edit.ts | 37 +++++++++++ .../backend/src/models/schema/note-edit.ts | 2 +- packages/backend/src/server/api/endpoints.ts | 2 + .../src/server/api/endpoints/notes/history.ts | 64 +++++++++++++++++++ 5 files changed, 106 insertions(+), 3 deletions(-) create mode 100644 packages/backend/src/models/repositories/note-edit.ts create mode 100644 packages/backend/src/server/api/endpoints/notes/history.ts diff --git a/packages/backend/src/models/index.ts b/packages/backend/src/models/index.ts index 5d4ff52198..c578d9d409 100644 --- a/packages/backend/src/models/index.ts +++ b/packages/backend/src/models/index.ts @@ -65,14 +65,14 @@ import { UserPending } from "./entities/user-pending.js"; 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"; +import { NoteEditRepository } from "./repositories/note-edit.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 NoteEdits = NoteEditRepository; export const NoteFiles = NoteFileRepository; export const NoteFavorites = NoteFavoriteRepository; export const NoteWatchings = db.getRepository(NoteWatching); diff --git a/packages/backend/src/models/repositories/note-edit.ts b/packages/backend/src/models/repositories/note-edit.ts new file mode 100644 index 0000000000..31a63af5b1 --- /dev/null +++ b/packages/backend/src/models/repositories/note-edit.ts @@ -0,0 +1,37 @@ +import { db } from "@/db/postgre.js"; +import { NoteEdit } from "@/models/entities/note-edit.js"; +import { awaitAll } from "@/prelude/await-all.js"; +import type { Packed } from "@/misc/schema.js"; + +export const NoteEditRepository = db.getRepository(NoteEdit).extend({ + async pack( + noteEdit: NoteEdit, + ) { + const packed: Packed<"NoteEdit"> = await awaitAll({ + id: noteEdit.id, + noteId: noteEdit.noteId, + updatedAt: noteEdit.updatedAt.toISOString(), + text: noteEdit.text, + cw: noteEdit.cw, + fileIds: noteEdit.fileIds, + }) + + return packed; + }, + async packMany( + noteEdits: NoteEdit[], + ) { + if (noteEdits.length === 0) return []; + + const promises = await Promise.allSettled( + noteEdits.map((n) => + this.pack(n) + ), + ); + + // filter out rejected promises, only keep fulfilled values + return promises.flatMap((result) => + result.status === "fulfilled" ? [result.value] : [], + ); + } +}); diff --git a/packages/backend/src/models/schema/note-edit.ts b/packages/backend/src/models/schema/note-edit.ts index e877f3f946..e02b866abd 100644 --- a/packages/backend/src/models/schema/note-edit.ts +++ b/packages/backend/src/models/schema/note-edit.ts @@ -16,7 +16,7 @@ export const packedNoteEdit = { }, note: { type: "object", - optional: false, + optional: true, nullable: false, ref: "Note", }, diff --git a/packages/backend/src/server/api/endpoints.ts b/packages/backend/src/server/api/endpoints.ts index 52dd3382f7..9a0de00b8b 100644 --- a/packages/backend/src/server/api/endpoints.ts +++ b/packages/backend/src/server/api/endpoints.ts @@ -240,6 +240,7 @@ import * as ep___notes_conversation from "./endpoints/notes/conversation.js"; import * as ep___notes_create from "./endpoints/notes/create.js"; import * as ep___notes_delete from "./endpoints/notes/delete.js"; import * as ep___notes_edit from "./endpoints/notes/edit.js"; +import * as ep___notes_history from "./endpoints/notes/history.js"; import * as ep___notes_favorites_create from "./endpoints/notes/favorites/create.js"; import * as ep___notes_favorites_delete from "./endpoints/notes/favorites/delete.js"; import * as ep___notes_featured from "./endpoints/notes/featured.js"; @@ -583,6 +584,7 @@ const eps = [ ["notes/create", ep___notes_create], ["notes/delete", ep___notes_delete], ["notes/edit", ep___notes_edit], + ["notes/history", ep___notes_history], ["notes/favorites/create", ep___notes_favorites_create], ["notes/favorites/delete", ep___notes_favorites_delete], ["notes/featured", ep___notes_featured], diff --git a/packages/backend/src/server/api/endpoints/notes/history.ts b/packages/backend/src/server/api/endpoints/notes/history.ts new file mode 100644 index 0000000000..e0c7be3df5 --- /dev/null +++ b/packages/backend/src/server/api/endpoints/notes/history.ts @@ -0,0 +1,64 @@ +import { NoteEdits } from "@/models/index.js"; +import define from "@/server/api/define.js"; +import { ApiError } from "@/server/api/error.js"; +import { getNote } from "@/server/api/common/getters.js"; +import type { NoteEdit } from "@/models/entities/note-edit.js"; + +export const meta = { + tags: ["notes"], + + requireCredential: false, + requireCredentialPrivateMode: true, + description: "Get edit history of a note", + + res: { + type: "array", + optional: false, + nullable: true, + items: { + type: "object", + optional: false, + nullable: false, + ref: "NoteEdit", + }, + }, + + errors: { + noSuchNote: { + message: "No such note.", + code: "NO_SUCH_NOTE", + id: "e1035875-9551-45ec-afa8-1ded1fcb53c8", + }, + }, +} as const; + +export const paramDef = { + type: "object", + properties: { + noteId: { + type: "string", + format: "misskey:id", + }, + limit: { type: "integer", minimum: 1, maximum: 100, default: 10 }, + offset: { type: "integer", default: 0 }, + }, + required: ["noteId"], +} as const; + +export default define(meta, paramDef, async (ps, user) => { + const note = await getNote(ps.noteId, user).catch((err) => { + if (err.id === "9725d0ce-ba28-4dde-95a7-2cbb2c15de24") + throw new ApiError(meta.errors.noSuchNote); + throw err; + }); + + const history: NoteEdit[] = await NoteEdits.find({ + where: { + noteId: note.id, + }, + take: ps.limit, + skip: ps.offset, + }); + + return await NoteEdits.packMany(history); +}); From bab704992ff71b823dee50e5c0de414b8c0eb910 Mon Sep 17 00:00:00 2001 From: Lhcfl Date: Tue, 26 Mar 2024 16:59:42 +0800 Subject: [PATCH 003/269] feat: add post history page --- locales/en-US.yml | 1 + locales/zh-CN.yml | 1 + .../src/server/api/endpoints/notes/history.ts | 3 + .../src/components/MkDateSeparatedList.vue | 2 +- packages/client/src/components/MkNote.vue | 8 +- packages/client/src/filters/note.ts | 9 +- packages/client/src/pages/note-history.vue | 123 ++++++++++++++++++ packages/client/src/router.ts | 5 + packages/client/src/scripts/get-note-menu.ts | 17 +++ packages/firefish-js/src/api.types.ts | 9 ++ packages/firefish-js/src/entities.ts | 13 +- 11 files changed, 186 insertions(+), 5 deletions(-) create mode 100644 packages/client/src/pages/note-history.vue diff --git a/locales/en-US.yml b/locales/en-US.yml index 88b44d2787..9a85183fe3 100644 --- a/locales/en-US.yml +++ b/locales/en-US.yml @@ -2232,3 +2232,4 @@ messagingUnencryptedInfo: "Chats on Firefish are not end-to-end encrypted. Don't any sensitive infomation over Firefish." autocorrectNoteLanguage: "Show a warning if the post language does not match the auto-detected result" incorrectLanguageWarning: "It looks like your post is in {detected}, but you selected {current}.\nWould you like to set the language to {detected} instead?" +noteEditHistory: "Post edit history" diff --git a/locales/zh-CN.yml b/locales/zh-CN.yml index f67bf4a600..85b73d7b45 100644 --- a/locales/zh-CN.yml +++ b/locales/zh-CN.yml @@ -2060,3 +2060,4 @@ noAltTextWarning: 有些附件没有描述。您是否忘记写描述了? showNoAltTextWarning: 当您尝试发布没有描述的帖子附件时显示警告 autocorrectNoteLanguage: 当帖子语言不符合自动检测的结果的时候显示警告 incorrectLanguageWarning: "看上去您帖子使用的语言是{detected},但您选择的语言是{current}。\n要改为以{detected}发帖吗?" +noteEditHistory: "帖子编辑历史" diff --git a/packages/backend/src/server/api/endpoints/notes/history.ts b/packages/backend/src/server/api/endpoints/notes/history.ts index e0c7be3df5..fb49b4a5b9 100644 --- a/packages/backend/src/server/api/endpoints/notes/history.ts +++ b/packages/backend/src/server/api/endpoints/notes/history.ts @@ -58,6 +58,9 @@ export default define(meta, paramDef, async (ps, user) => { }, take: ps.limit, skip: ps.offset, + order: { + id: "DESC" + }, }); return await NoteEdits.packMany(history); diff --git a/packages/client/src/components/MkDateSeparatedList.vue b/packages/client/src/components/MkDateSeparatedList.vue index e40d64dd7d..c6b75a20fc 100644 --- a/packages/client/src/components/MkDateSeparatedList.vue +++ b/packages/client/src/components/MkDateSeparatedList.vue @@ -10,7 +10,7 @@ export default defineComponent({ props: { items: { type: Array as PropType< - { id: string; createdAt: string; _shouldInsertAd_: boolean }[] + { id: string; createdAt: string; _shouldInsertAd_?: boolean }[] >, required: true, }, diff --git a/packages/client/src/components/MkNote.vue b/packages/client/src/components/MkNote.vue index e13050206b..723fabbeee 100644 --- a/packages/client/src/components/MkNote.vue +++ b/packages/client/src/components/MkNote.vue @@ -154,7 +154,12 @@ {{ appearNote.channel.name }} -