From 85e985d13fe8c84e06c4aeea3c31ee92ef3cef30 Mon Sep 17 00:00:00 2001 From: Johann150 Date: Sun, 8 Jan 2023 17:57:36 +0100 Subject: [PATCH 01/46] server: change data structure to track deletion completion --- .../1673201544000-deletion-progress.js | 18 ++++++++++++++++++ packages/backend/src/models/entities/user.ts | 8 ++++---- .../backend/src/models/repositories/user.ts | 2 +- .../remote/activitypub/kernel/delete/actor.ts | 2 +- .../api/endpoints/admin/accounts/delete.ts | 3 ++- .../server/api/endpoints/i/delete-account.ts | 2 +- .../backend/src/services/delete-account.ts | 2 +- 7 files changed, 28 insertions(+), 9 deletions(-) create mode 100644 packages/backend/migration/1673201544000-deletion-progress.js diff --git a/packages/backend/migration/1673201544000-deletion-progress.js b/packages/backend/migration/1673201544000-deletion-progress.js new file mode 100644 index 000000000..90aa5cbf8 --- /dev/null +++ b/packages/backend/migration/1673201544000-deletion-progress.js @@ -0,0 +1,18 @@ +export class deletionProgress1673201544000 { + name = 'deletionProgress1673201544000'; + + async up(queryRunner) { + await queryRunner.query(`ALTER TABLE "user" RENAME COLUMN "isDeleted" TO "isDeletedOld"`); + await queryRunner.query(`ALTER TABLE "user" ADD "isDeleted" integer`); + await queryRunner.query(`UPDATE "user" SET "isDeleted" = CASE WHEN "host" IS NULL THEN -1 ELSE 0 END WHERE "isDeletedOld"`); + await queryRunner.query(`ALTER TABLE "user" DROP COLUMN "isDeletedOld"`); + } + + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "user" RENAME COLUMN "isDeleted" TO "isDeletedOld"`); + await queryRunner.query(`ALTER TABLE "user" ADD "isDeleted" boolean NOT NULL DEFAULT false`); + await queryRunner.query(`UPDATE "user" SET "isDeleted" = "isDeletedOld" IS NOT NULL`); + await queryRunner.query(`ALTER TABLE "user" DROP COLUMN "isDeletedOld"`); + } +} + diff --git a/packages/backend/src/models/entities/user.ts b/packages/backend/src/models/entities/user.ts index 0aee4c90b..374928fe1 100644 --- a/packages/backend/src/models/entities/user.ts +++ b/packages/backend/src/models/entities/user.ts @@ -163,11 +163,11 @@ export class User { // Indicates the user was deleted by an admin. // The users' data is not deleted from the database to keep them from reappearing. // A hard delete of the record may follow if we receive a matching Delete activity. - @Column('boolean', { - default: false, - comment: 'Whether the User is deleted.', + @Column('integer', { + nullable: true, + comment: 'How many delivery jobs are outstanding before the deletion is completed.', }) - public isDeleted: boolean; + public isDeleted: number | null; @Column('varchar', { length: 128, array: true, default: '{}', diff --git a/packages/backend/src/models/repositories/user.ts b/packages/backend/src/models/repositories/user.ts index fd308dcae..397ee2105 100644 --- a/packages/backend/src/models/repositories/user.ts +++ b/packages/backend/src/models/repositories/user.ts @@ -369,7 +369,7 @@ export const UserRepository = db.getRepository(User).extend({ autoAcceptFollowed: profile!.autoAcceptFollowed, noCrawle: profile!.noCrawle, isExplorable: user.isExplorable, - isDeleted: user.isDeleted, + isDeleted: user.isDeleted != null, hideOnlineStatus: user.hideOnlineStatus, hasUnreadSpecifiedNotes: NoteUnreads.count({ where: { userId: user.id, isSpecified: true }, diff --git a/packages/backend/src/remote/activitypub/kernel/delete/actor.ts b/packages/backend/src/remote/activitypub/kernel/delete/actor.ts index ea75a9739..f69d87d2c 100644 --- a/packages/backend/src/remote/activitypub/kernel/delete/actor.ts +++ b/packages/backend/src/remote/activitypub/kernel/delete/actor.ts @@ -16,7 +16,7 @@ export async function deleteActor(actor: CacheableRemoteUser, uri: string): Prom // anyway, the user is gone now so dont care return 'ok: gone'; } - if (user.isDeleted) { + if (user.isDeleted != null) { // the actual deletion already happened by an admin, just delete the record await Users.delete(actor.id); } else { diff --git a/packages/backend/src/server/api/endpoints/admin/accounts/delete.ts b/packages/backend/src/server/api/endpoints/admin/accounts/delete.ts index 4f5ea5568..88065f81f 100644 --- a/packages/backend/src/server/api/endpoints/admin/accounts/delete.ts +++ b/packages/backend/src/server/api/endpoints/admin/accounts/delete.ts @@ -1,3 +1,4 @@ +import { IsNull } from 'typeorm'; import { Users } from '@/models/index.js'; import { ApiError } from '@/server/api/error.js'; import { deleteAccount } from '@/services/delete-account.js'; @@ -24,7 +25,7 @@ export const paramDef = { export default define(meta, paramDef, async (ps) => { const user = await Users.findOneBy({ id: ps.userId, - isDeleted: false, + isDeleted: IsNull(), }); if (user == null) { diff --git a/packages/backend/src/server/api/endpoints/i/delete-account.ts b/packages/backend/src/server/api/endpoints/i/delete-account.ts index 5dae620ae..a77afc8be 100644 --- a/packages/backend/src/server/api/endpoints/i/delete-account.ts +++ b/packages/backend/src/server/api/endpoints/i/delete-account.ts @@ -27,7 +27,7 @@ export default define(meta, paramDef, async (ps, user) => { Users.findOneByOrFail({ id: user.id }), ]); - if (userDetailed.isDeleted) { + if (userDetailed.isDeleted != null) { return; } diff --git a/packages/backend/src/services/delete-account.ts b/packages/backend/src/services/delete-account.ts index 9c8d4c277..bf95b2bc0 100644 --- a/packages/backend/src/services/delete-account.ts +++ b/packages/backend/src/services/delete-account.ts @@ -8,7 +8,7 @@ export async function deleteAccount(user: { host: string | null; }): Promise { await Users.update(user.id, { - isDeleted: true, + isDeleted: -1, }); if (Users.isLocalUser(user)) { From 80f72e21cd5772ba841cbd2dbf510432d16945da Mon Sep 17 00:00:00 2001 From: Johann150 Date: Sun, 8 Jan 2023 18:50:35 +0100 Subject: [PATCH 02/46] server: track deletion completion --- packages/backend/src/queue/index.ts | 26 +++++++++++++++---- packages/backend/src/queue/types.ts | 2 ++ .../src/remote/activitypub/deliver-manager.ts | 18 ++++++++----- packages/backend/src/services/suspend-user.ts | 5 +++- 4 files changed, 38 insertions(+), 13 deletions(-) diff --git a/packages/backend/src/queue/index.ts b/packages/backend/src/queue/index.ts index 57de62a79..ab247209f 100644 --- a/packages/backend/src/queue/index.ts +++ b/packages/backend/src/queue/index.ts @@ -1,7 +1,9 @@ import httpSignature from '@peertube/http-signature'; import { v4 as uuid } from 'uuid'; +import Bull from 'bull'; import config from '@/config/index.js'; +import { Users } from '@/models/index.js'; import { DriveFile } from '@/models/entities/drive-file.js'; import { Webhook, webhookEventTypes } from '@/models/entities/webhook.js'; import { IActivity } from '@/remote/activitypub/type.js'; @@ -18,7 +20,7 @@ import { endedPollNotification } from './processors/ended-poll-notification.js'; import { queueLogger } from './logger.js'; import { getJobInfo } from './get-job-info.js'; import { systemQueue, dbQueue, deliverQueue, inboxQueue, objectStorageQueue, endedPollNotificationQueue, webhookDeliverQueue } from './queues.js'; -import { ThinUser } from './types.js'; +import { DeliverJobData, ThinUser } from './types.js'; function renderError(e: Error): any { return { @@ -35,6 +37,12 @@ const inboxLogger = queueLogger.createSubLogger('inbox'); const dbLogger = queueLogger.createSubLogger('db'); const objectStorageLogger = queueLogger.createSubLogger('objectStorage'); +async function deletionRefCount(job: Bull.Job): Promise { + if (job.data.deletingUserId) { + await Users.decrement({ id: job.data.deletingUserId }, 'isDeleted', 1); + } +} + systemQueue .on('waiting', (jobId) => systemLogger.debug(`waiting id=${jobId}`)) .on('active', (job) => systemLogger.debug(`active id=${job.id}`)) @@ -46,8 +54,14 @@ systemQueue deliverQueue .on('waiting', (jobId) => deliverLogger.debug(`waiting id=${jobId}`)) .on('active', (job) => deliverLogger.debug(`active ${getJobInfo(job, true)} to=${job.data.to}`)) - .on('completed', (job, result) => deliverLogger.debug(`completed(${result}) ${getJobInfo(job, true)} to=${job.data.to}`)) - .on('failed', (job, err) => deliverLogger.warn(`failed(${err}) ${getJobInfo(job)} to=${job.data.to}`)) + .on('completed', async (job, result) => { + deliverLogger.debug(`completed(${result}) ${getJobInfo(job, true)} to=${job.data.to}`); + await deletionRefCount(job); + }) + .on('failed', async (job, err) => { + deliverLogger.warn(`failed(${err}) ${getJobInfo(job)} to=${job.data.to}`); + await deletionRefCount(job); + }) .on('error', (job: any, err: Error) => deliverLogger.error(`error ${err}`, { job, e: renderError(err) })) .on('stalled', (job) => deliverLogger.warn(`stalled ${getJobInfo(job)} to=${job.data.to}`)); @@ -83,7 +97,7 @@ webhookDeliverQueue .on('error', (job: any, err: Error) => webhookLogger.error(`error ${err}`, { job, e: renderError(err) })) .on('stalled', (job) => webhookLogger.warn(`stalled ${getJobInfo(job)} to=${job.data.to}`)); -export function deliver(user: ThinUser, content: unknown, to: string | null) { +export function deliver(user: ThinUser, content: unknown, to: string | null, deletingUserId?: string) { if (content == null) return null; if (to == null) return null; @@ -93,6 +107,7 @@ export function deliver(user: ThinUser, content: unknown, to: string | null) { }, content, to, + deletingUserId, }; return deliverQueue.add(data, { @@ -326,8 +341,9 @@ export default function() { } export function destroy() { - deliverQueue.once('cleaned', (jobs, status) => { + deliverQueue.once('cleaned', async (jobs, status) => { deliverLogger.succ(`Cleaned ${jobs.length} ${status} jobs`); + await Promise.all(jobs.map(job => deletionRefCount(job)); }); deliverQueue.clean(0, 'delayed'); diff --git a/packages/backend/src/queue/types.ts b/packages/backend/src/queue/types.ts index 82bd28703..d745a1fc7 100644 --- a/packages/backend/src/queue/types.ts +++ b/packages/backend/src/queue/types.ts @@ -12,6 +12,8 @@ export type DeliverJobData = { content: unknown; /** inbox URL to deliver */ to: string; + /** set if this job is part of a user deletion, on completion or failure the isDeleted field needs to be decremented */ + deletingUserId?: string; }; export type InboxJobData = { diff --git a/packages/backend/src/remote/activitypub/deliver-manager.ts b/packages/backend/src/remote/activitypub/deliver-manager.ts index 4bc651c98..dfadef150 100644 --- a/packages/backend/src/remote/activitypub/deliver-manager.ts +++ b/packages/backend/src/remote/activitypub/deliver-manager.ts @@ -88,10 +88,10 @@ export class DeliverManager { /** * Execute delivers */ - public async execute() { + public async execute(deletingUserId?: string) { if (!Users.isLocalUser(this.actor)) return; - const inboxes = new Set(); + let inboxes = new Set(); /* build inbox list @@ -150,13 +150,17 @@ export class DeliverManager { )), ); - // deliver - for (const inbox of inboxes) { - // skip instances as indicated - if (instancesToSkip.includes(new URL(inbox).host)) continue; + inboxes = inboxes.entries() + .filter(inbox => !instancesToSkip.includes(new URL(inbox).host)); - deliver(this.actor, this.activity, inbox); + if (deletingUserId) { + await Users.update(deletingUserId, { + // set deletion job count for reference counting before queueing jobs + isDeleted: inboxes.length, + }); } + + inboxes.forEach(inbox => deliver(this.actor, this.activity, inbox, deletingUserId)); } } diff --git a/packages/backend/src/services/suspend-user.ts b/packages/backend/src/services/suspend-user.ts index 11e6266a0..7ed6bff80 100644 --- a/packages/backend/src/services/suspend-user.ts +++ b/packages/backend/src/services/suspend-user.ts @@ -6,6 +6,9 @@ import { User } from '@/models/entities/user.js'; import { Users } from '@/models/index.js'; import { publishInternalEvent } from '@/services/stream.js'; +/** + * Sends an internal event and for local users queues the delete activites. + */ export async function doPostSuspend(user: { id: User['id']; host: User['host'] }): Promise { publishInternalEvent('userChangeSuspendedState', { id: user.id, isSuspended: true }); @@ -15,6 +18,6 @@ export async function doPostSuspend(user: { id: User['id']; host: User['host'] } // deliver to all of known network const dm = new DeliverManager(user, content); dm.addEveryone(); - await dm.execute(); + await dm.execute(user.id); } } From b245d39b6e317ad6422f98e7d36fa1d74dc88296 Mon Sep 17 00:00:00 2001 From: Johann150 Date: Sun, 8 Jan 2023 18:53:54 +0100 Subject: [PATCH 03/46] server: delete records of fully deleted users --- .../backend/src/queue/processors/system/check-expired.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/backend/src/queue/processors/system/check-expired.ts b/packages/backend/src/queue/processors/system/check-expired.ts index eeb6149bb..71bd498e9 100644 --- a/packages/backend/src/queue/processors/system/check-expired.ts +++ b/packages/backend/src/queue/processors/system/check-expired.ts @@ -1,6 +1,6 @@ import Bull from 'bull'; import { In, LessThan } from 'typeorm'; -import { AttestationChallenges, AuthSessions, Mutings, Notifications, PasswordResetRequests, Signins } from '@/models/index.js'; +import { AttestationChallenges, AuthSessions, Mutings, Notifications, PasswordResetRequests, Signins, Users } from '@/models/index.js'; import { publishUserEvent } from '@/services/stream.js'; import { MINUTE, MONTH } from '@/const.js'; import { queueLogger } from '@/queue/logger.js'; @@ -52,6 +52,11 @@ export async function checkExpired(job: Bull.Job>, done: createdAt: OlderThan(3 * MONTH), }); + await Users.delete({ + // delete users where the deletion status reference count has come down to zero + isDeleted: 0, + }); + logger.succ('Deleted expired data.'); done(); From 48163872eddf803e36aea5221e33c29ba9d4fb51 Mon Sep 17 00:00:00 2001 From: Johann150 Date: Mon, 16 Jan 2023 18:30:45 +0100 Subject: [PATCH 04/46] client: remove galleries --- locales/en-US.yml | 10 - .../src/components/gallery-post-preview.vue | 113 -------- packages/client/src/menu.ts | 5 - packages/client/src/pages/gallery/edit.vue | 153 ---------- packages/client/src/pages/gallery/index.vue | 137 --------- packages/client/src/pages/gallery/post.vue | 264 ------------------ packages/client/src/pages/user/gallery.vue | 38 --- packages/client/src/pages/user/index.vue | 6 - packages/client/src/router.ts | 14 - 9 files changed, 740 deletions(-) delete mode 100644 packages/client/src/components/gallery-post-preview.vue delete mode 100644 packages/client/src/pages/gallery/edit.vue delete mode 100644 packages/client/src/pages/gallery/index.vue delete mode 100644 packages/client/src/pages/gallery/post.vue delete mode 100644 packages/client/src/pages/user/gallery.vue diff --git a/locales/en-US.yml b/locales/en-US.yml index 45d6af9bf..9999a5115 100644 --- a/locales/en-US.yml +++ b/locales/en-US.yml @@ -679,7 +679,6 @@ editCode: "Edit code" apply: "Apply" receiveAnnouncementFromInstance: "Receive notifications from this instance" emailNotification: "Email notifications" -publish: "Publish" useReactionPickerForContextMenu: "Open reaction picker on right-click" typingUsers: "{users} is/are typing..." jumpToSpecifiedDate: "Jump to specific date" @@ -720,11 +719,7 @@ switch: "Switch" noMaintainerInformationWarning: "Maintainer information is not configured." noBotProtectionWarning: "Bot protection is not configured." configure: "Configure" -postToGallery: "Create new gallery post" -attachmentRequired: "At least 1 attachment is required." -gallery: "Gallery" recentPosts: "Recent posts" -popularPosts: "Popular posts" shareWithNote: "Share with note" emailNotConfiguredWarning: "Email address not set." ratio: "Ratio" @@ -863,11 +858,6 @@ _forgotPassword: \ instance administrator instead." contactAdmin: "This instance does not support using email addresses, please contact\ \ the instance administrator to reset your password instead." -_gallery: - my: "My Gallery" - liked: "Liked Posts" - like: "Like" - unlike: "Remove like" _email: _follow: title: "You've got a new follower" diff --git a/packages/client/src/components/gallery-post-preview.vue b/packages/client/src/components/gallery-post-preview.vue deleted file mode 100644 index 6acd9646b..000000000 --- a/packages/client/src/components/gallery-post-preview.vue +++ /dev/null @@ -1,113 +0,0 @@ - - - - - diff --git a/packages/client/src/menu.ts b/packages/client/src/menu.ts index b730fdd8e..1de63b4dc 100644 --- a/packages/client/src/menu.ts +++ b/packages/client/src/menu.ts @@ -131,11 +131,6 @@ export const menuDef = reactive({ icon: 'fas fa-file-alt', to: '/pages', }, - gallery: { - title: 'gallery', - icon: 'fas fa-icons', - to: '/gallery', - }, clips: { title: 'clip', icon: 'fas fa-paperclip', diff --git a/packages/client/src/pages/gallery/edit.vue b/packages/client/src/pages/gallery/edit.vue deleted file mode 100644 index 964f6d4ff..000000000 --- a/packages/client/src/pages/gallery/edit.vue +++ /dev/null @@ -1,153 +0,0 @@ - - - - - diff --git a/packages/client/src/pages/gallery/index.vue b/packages/client/src/pages/gallery/index.vue deleted file mode 100644 index 2e2a72897..000000000 --- a/packages/client/src/pages/gallery/index.vue +++ /dev/null @@ -1,137 +0,0 @@ - - - - - diff --git a/packages/client/src/pages/gallery/post.vue b/packages/client/src/pages/gallery/post.vue deleted file mode 100644 index 2886321d2..000000000 --- a/packages/client/src/pages/gallery/post.vue +++ /dev/null @@ -1,264 +0,0 @@ - - - - - diff --git a/packages/client/src/pages/user/gallery.vue b/packages/client/src/pages/user/gallery.vue deleted file mode 100644 index 1d053b279..000000000 --- a/packages/client/src/pages/user/gallery.vue +++ /dev/null @@ -1,38 +0,0 @@ - - - - - diff --git a/packages/client/src/pages/user/index.vue b/packages/client/src/pages/user/index.vue index 3b47bb91a..24c88e75d 100644 --- a/packages/client/src/pages/user/index.vue +++ b/packages/client/src/pages/user/index.vue @@ -8,7 +8,6 @@ - @@ -33,7 +32,6 @@ const XHome = defineAsyncComponent(() => import('./home.vue')); const XReactions = defineAsyncComponent(() => import('./reactions.vue')); const XClips = defineAsyncComponent(() => import('./clips.vue')); const XPages = defineAsyncComponent(() => import('./pages.vue')); -const XGallery = defineAsyncComponent(() => import('./gallery.vue')); const props = withDefaults(defineProps<{ acct: string; @@ -82,10 +80,6 @@ const headerTabs = $computed(() => [{ key: 'pages', title: i18n.ts.pages, icon: 'fas fa-file-alt', -}, { - key: 'gallery', - title: i18n.ts.gallery, - icon: 'fas fa-icons', }]); definePageMetadata(computed(() => user ? { diff --git a/packages/client/src/router.ts b/packages/client/src/router.ts index 791076e71..10869a3cf 100644 --- a/packages/client/src/router.ts +++ b/packages/client/src/router.ts @@ -123,20 +123,6 @@ export const routes = [{ }, { path: '/pages', component: page(() => import('./pages/pages.vue')), -}, { - path: '/gallery/:postId/edit', - component: page(() => import('./pages/gallery/edit.vue')), - loginRequired: true, -}, { - path: '/gallery/new', - component: page(() => import('./pages/gallery/edit.vue')), - loginRequired: true, -}, { - path: '/gallery/:postId', - component: page(() => import('./pages/gallery/post.vue')), -}, { - path: '/gallery', - component: page(() => import('./pages/gallery/index.vue')), }, { path: '/channels/:channelId/edit', component: page(() => import('./pages/channel-editor.vue')), From 70fb1e9a5cf6633e8b77794b561c9fa529355e51 Mon Sep 17 00:00:00 2001 From: Johann150 Date: Mon, 16 Jan 2023 18:47:29 +0100 Subject: [PATCH 05/46] foundkey-js: remove galleries --- locales/en-US.yml | 4 ---- packages/foundkey-js/src/api.types.ts | 14 +------------- packages/foundkey-js/src/consts.ts | 4 ---- packages/foundkey-js/src/entities.ts | 2 -- 4 files changed, 1 insertion(+), 23 deletions(-) diff --git a/locales/en-US.yml b/locales/en-US.yml index 9999a5115..14df6ca9c 100644 --- a/locales/en-US.yml +++ b/locales/en-US.yml @@ -1099,10 +1099,6 @@ _permissions: "write:user-groups": "Create, modify, delete, transfer, join and leave groups. Invite and ban others from groups. Accept and reject group invitations." "read:channels": "List and read followed and joined channels" "write:channels": "Create, modify, follow and unfollow channels" - "read:gallery": "List and read gallery posts" - "write:gallery": "Create, modify and delete gallery posts" - "read:gallery-likes": "List and read gallery post likes" - "write:gallery-likes": "Like and unlike gallery posts" _auth: shareAccess: "Would you like to authorize \"{name}\" to access this account?" shareAccessAsk: "Are you sure you want to authorize this application to access your\ diff --git a/packages/foundkey-js/src/api.types.ts b/packages/foundkey-js/src/api.types.ts index 8798113a5..64a5f98aa 100644 --- a/packages/foundkey-js/src/api.types.ts +++ b/packages/foundkey-js/src/api.types.ts @@ -1,5 +1,5 @@ import { - Ad, Announcement, Antenna, App, AuthSession, Blocking, Channel, Clip, DateString, DetailedInstanceMetadata, DriveFile, DriveFolder, Following, FollowingFolloweePopulated, FollowingFollowerPopulated, FollowRequest, GalleryPost, Instance, InstanceMetadata, + Ad, Announcement, Antenna, App, AuthSession, Blocking, Channel, Clip, DateString, DetailedInstanceMetadata, DriveFile, DriveFolder, Following, FollowingFolloweePopulated, FollowingFollowerPopulated, FollowRequest, Instance, InstanceMetadata, LiteInstanceMetadata, MeDetailed, Note, NoteFavorite, OriginType, Page, ServerInfo, Stats, User, UserDetailed, UserGroup, UserList, UserSorting, Notification, NoteReaction, Signin, MessagingMessage, @@ -282,15 +282,6 @@ export type Endpoints = { 'following/requests/cancel': { req: { userId: User['id'] }; res: User; }; 'following/requests/list': { req: NoParams; res: FollowRequest[]; }; 'following/requests/reject': { req: { userId: User['id'] }; res: null; }; - 'gallery/featured': { req: TODO; res: TODO; }; - 'gallery/popular': { req: TODO; res: TODO; }; - 'gallery/posts': { req: TODO; res: TODO; }; - 'gallery/posts/create': { req: TODO; res: TODO; }; - 'gallery/posts/delete': { req: { postId: GalleryPost['id'] }; res: null; }; - 'gallery/posts/like': { req: TODO; res: TODO; }; - 'gallery/posts/show': { req: TODO; res: TODO; }; - 'gallery/posts/unlike': { req: TODO; res: TODO; }; - 'gallery/posts/update': { req: TODO; res: TODO; }; 'get-online-users-count': { req: TODO; res: TODO; }; 'hashtags/list': { req: TODO; res: TODO; }; 'hashtags/search': { req: TODO; res: TODO; }; @@ -315,8 +306,6 @@ export type Endpoints = { 'i/export-notes': { req: TODO; res: TODO; }; 'i/export-user-lists': { req: TODO; res: TODO; }; 'i/favorites': { req: { limit?: number; sinceId?: NoteFavorite['id']; untilId?: NoteFavorite['id']; }; res: NoteFavorite[]; }; - 'i/gallery/likes': { req: TODO; res: TODO; }; - 'i/gallery/posts': { req: TODO; res: TODO; }; 'i/get-word-muted-notes-count': { req: TODO; res: TODO; }; 'i/import-blocking': { req: TODO; res: TODO; }; 'i/import-following': { req: TODO; res: TODO; }; @@ -488,7 +477,6 @@ export type Endpoints = { 'users/clips': { req: TODO; res: TODO; }; 'users/followers': { req: { userId?: User['id']; username?: User['username']; host?: User['host'] | null; limit?: number; sinceId?: Following['id']; untilId?: Following['id']; }; res: FollowingFollowerPopulated[]; }; 'users/following': { req: { userId?: User['id']; username?: User['username']; host?: User['host'] | null; limit?: number; sinceId?: Following['id']; untilId?: Following['id']; }; res: FollowingFolloweePopulated[]; }; - 'users/gallery/posts': { req: TODO; res: TODO; }; 'users/groups/create': { req: TODO; res: TODO; }; 'users/groups/delete': { req: { groupId: UserGroup['id'] }; res: null; }; 'users/groups/invitations/accept': { req: TODO; res: TODO; }; diff --git a/packages/foundkey-js/src/consts.ts b/packages/foundkey-js/src/consts.ts index 645bdf223..a90b8720a 100644 --- a/packages/foundkey-js/src/consts.ts +++ b/packages/foundkey-js/src/consts.ts @@ -35,8 +35,4 @@ export const permissions = [ 'write:user-groups', 'read:channels', 'write:channels', - 'read:gallery', - 'write:gallery', - 'read:gallery-likes', - 'write:gallery-likes', ]; diff --git a/packages/foundkey-js/src/entities.ts b/packages/foundkey-js/src/entities.ts index c92e60082..3d2404b47 100644 --- a/packages/foundkey-js/src/entities.ts +++ b/packages/foundkey-js/src/entities.ts @@ -126,8 +126,6 @@ export type DriveFile = { export type DriveFolder = TODO; -export type GalleryPost = TODO; - export type Note = { id: ID; createdAt: DateString; From 2bbb85b47209bb01d9b822d536a9f96028a4161d Mon Sep 17 00:00:00 2001 From: Johann150 Date: Mon, 16 Jan 2023 18:53:57 +0100 Subject: [PATCH 06/46] backend: remove galleries --- packages/backend/src/db/postgre.ts | 4 - packages/backend/src/misc/api-permissions.ts | 4 - packages/backend/src/misc/schema.ts | 2 - .../src/models/entities/gallery-like.ts | 33 -------- .../src/models/entities/gallery-post.ts | 79 ------------------- packages/backend/src/models/index.ts | 4 - .../src/models/repositories/gallery-like.ts | 24 ------ .../src/models/repositories/gallery-post.ts | 39 --------- .../backend/src/models/schema/gallery-post.ts | 69 ---------------- packages/backend/src/server/api/endpoints.ts | 24 ------ .../server/api/endpoints/gallery/featured.ts | 37 --------- .../server/api/endpoints/gallery/popular.ts | 35 -------- .../src/server/api/endpoints/gallery/posts.ts | 37 --------- .../api/endpoints/gallery/posts/create.ts | 72 ----------------- .../api/endpoints/gallery/posts/delete.ts | 33 -------- .../api/endpoints/gallery/posts/like.ts | 46 ----------- .../api/endpoints/gallery/posts/show.ts | 36 --------- .../api/endpoints/gallery/posts/unlike.ts | 39 --------- .../api/endpoints/gallery/posts/update.ts | 75 ------------------ .../server/api/endpoints/i/gallery/likes.ts | 55 ------------- .../server/api/endpoints/i/gallery/posts.ts | 43 ---------- .../api/endpoints/users/gallery/posts.ts | 42 ---------- packages/backend/src/server/api/error.ts | 8 +- packages/backend/src/server/web/index.ts | 27 +------ .../src/server/web/views/gallery-post.pug | 33 -------- 25 files changed, 3 insertions(+), 897 deletions(-) delete mode 100644 packages/backend/src/models/entities/gallery-like.ts delete mode 100644 packages/backend/src/models/entities/gallery-post.ts delete mode 100644 packages/backend/src/models/repositories/gallery-like.ts delete mode 100644 packages/backend/src/models/repositories/gallery-post.ts delete mode 100644 packages/backend/src/models/schema/gallery-post.ts delete mode 100644 packages/backend/src/server/api/endpoints/gallery/featured.ts delete mode 100644 packages/backend/src/server/api/endpoints/gallery/popular.ts delete mode 100644 packages/backend/src/server/api/endpoints/gallery/posts.ts delete mode 100644 packages/backend/src/server/api/endpoints/gallery/posts/create.ts delete mode 100644 packages/backend/src/server/api/endpoints/gallery/posts/delete.ts delete mode 100644 packages/backend/src/server/api/endpoints/gallery/posts/like.ts delete mode 100644 packages/backend/src/server/api/endpoints/gallery/posts/show.ts delete mode 100644 packages/backend/src/server/api/endpoints/gallery/posts/unlike.ts delete mode 100644 packages/backend/src/server/api/endpoints/gallery/posts/update.ts delete mode 100644 packages/backend/src/server/api/endpoints/i/gallery/likes.ts delete mode 100644 packages/backend/src/server/api/endpoints/i/gallery/posts.ts delete mode 100644 packages/backend/src/server/api/endpoints/users/gallery/posts.ts delete mode 100644 packages/backend/src/server/web/views/gallery-post.pug diff --git a/packages/backend/src/db/postgre.ts b/packages/backend/src/db/postgre.ts index 90b123ae8..b59142597 100644 --- a/packages/backend/src/db/postgre.ts +++ b/packages/backend/src/db/postgre.ts @@ -50,8 +50,6 @@ import { UserSecurityKey } from '@/models/entities/user-security-key.js'; import { AttestationChallenge } from '@/models/entities/attestation-challenge.js'; import { Page } from '@/models/entities/page.js'; import { PageLike } from '@/models/entities/page-like.js'; -import { GalleryPost } from '@/models/entities/gallery-post.js'; -import { GalleryLike } from '@/models/entities/gallery-like.js'; import { ModerationLog } from '@/models/entities/moderation-log.js'; import { UsedUsername } from '@/models/entities/used-username.js'; import { Announcement } from '@/models/entities/announcement.js'; @@ -143,8 +141,6 @@ export const entities = [ NoteUnread, Page, PageLike, - GalleryPost, - GalleryLike, DriveFile, DriveFolder, Poll, diff --git a/packages/backend/src/misc/api-permissions.ts b/packages/backend/src/misc/api-permissions.ts index 160cdf9fd..d7c115a50 100644 --- a/packages/backend/src/misc/api-permissions.ts +++ b/packages/backend/src/misc/api-permissions.ts @@ -27,9 +27,5 @@ export const kinds = [ 'write:user-groups', 'read:channels', 'write:channels', - 'read:gallery', - 'write:gallery', - 'read:gallery-likes', - 'write:gallery-likes', ]; // IF YOU ADD KINDS(PERMISSIONS), YOU MUST ADD TRANSLATIONS (under _permissions). diff --git a/packages/backend/src/misc/schema.ts b/packages/backend/src/misc/schema.ts index ad5dcb067..bad291b4a 100644 --- a/packages/backend/src/misc/schema.ts +++ b/packages/backend/src/misc/schema.ts @@ -28,7 +28,6 @@ import { packedAntennaSchema } from '@/models/schema/antenna.js'; import { packedClipSchema } from '@/models/schema/clip.js'; import { packedFederationInstanceSchema } from '@/models/schema/federation-instance.js'; import { packedQueueCountSchema } from '@/models/schema/queue.js'; -import { packedGalleryPostSchema } from '@/models/schema/gallery-post.js'; import { packedEmojiSchema } from '@/models/schema/emoji.js'; export const refs = { @@ -61,7 +60,6 @@ export const refs = { Antenna: packedAntennaSchema, Clip: packedClipSchema, FederationInstance: packedFederationInstanceSchema, - GalleryPost: packedGalleryPostSchema, Emoji: packedEmojiSchema, }; diff --git a/packages/backend/src/models/entities/gallery-like.ts b/packages/backend/src/models/entities/gallery-like.ts deleted file mode 100644 index 259981392..000000000 --- a/packages/backend/src/models/entities/gallery-like.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; -import { id } from '../id.js'; -import { User } from './user.js'; -import { GalleryPost } from './gallery-post.js'; - -@Entity() -@Index(['userId', 'postId'], { unique: true }) -export class GalleryLike { - @PrimaryColumn(id()) - public id: string; - - @Column('timestamp with time zone') - public createdAt: Date; - - @Index() - @Column(id()) - public userId: User['id']; - - @ManyToOne(() => User, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public user: User | null; - - @Column(id()) - public postId: GalleryPost['id']; - - @ManyToOne(() => GalleryPost, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public post: GalleryPost | null; -} diff --git a/packages/backend/src/models/entities/gallery-post.ts b/packages/backend/src/models/entities/gallery-post.ts deleted file mode 100644 index 315bcd371..000000000 --- a/packages/backend/src/models/entities/gallery-post.ts +++ /dev/null @@ -1,79 +0,0 @@ -import { Entity, Index, JoinColumn, Column, PrimaryColumn, ManyToOne } from 'typeorm'; -import { id } from '../id.js'; -import { User } from './user.js'; -import { DriveFile } from './drive-file.js'; - -@Entity() -export class GalleryPost { - @PrimaryColumn(id()) - public id: string; - - @Index() - @Column('timestamp with time zone', { - comment: 'The created date of the GalleryPost.', - }) - public createdAt: Date; - - @Index() - @Column('timestamp with time zone', { - comment: 'The updated date of the GalleryPost.', - }) - public updatedAt: Date; - - @Column('varchar', { - length: 256, - }) - public title: string; - - @Column('varchar', { - length: 2048, nullable: true, - }) - public description: string | null; - - @Index() - @Column({ - ...id(), - comment: 'The ID of author.', - }) - public userId: User['id']; - - @ManyToOne(() => User, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public user: User | null; - - @Index() - @Column({ - ...id(), - array: true, default: '{}', - }) - public fileIds: DriveFile['id'][]; - - @Index() - @Column('boolean', { - default: false, - comment: 'Whether the post is sensitive.', - }) - public isSensitive: boolean; - - @Index() - @Column('integer', { - default: 0, - }) - public likedCount: number; - - @Index() - @Column('varchar', { - length: 128, array: true, default: '{}', - }) - public tags: string[]; - - constructor(data: Partial) { - if (data == null) return; - - for (const [k, v] of Object.entries(data)) { - (this as any)[k] = v; - } - } -} diff --git a/packages/backend/src/models/index.ts b/packages/backend/src/models/index.ts index 993f3e0f2..f4aa84d5b 100644 --- a/packages/backend/src/models/index.ts +++ b/packages/backend/src/models/index.ts @@ -42,8 +42,6 @@ import { UserSecurityKey } from './entities/user-security-key.js'; import { HashtagRepository } from './repositories/hashtag.js'; import { PageRepository } from './repositories/page.js'; import { PageLikeRepository } from './repositories/page-like.js'; -import { GalleryPostRepository } from './repositories/gallery-post.js'; -import { GalleryLikeRepository } from './repositories/gallery-like.js'; import { ModerationLogRepository } from './repositories/moderation-logs.js'; import { UsedUsername } from './entities/used-username.js'; import { ClipRepository } from './repositories/clip.js'; @@ -108,8 +106,6 @@ export const Signins = (SigninRepository); export const MessagingMessages = (MessagingMessageRepository); export const Pages = (PageRepository); export const PageLikes = (PageLikeRepository); -export const GalleryPosts = (GalleryPostRepository); -export const GalleryLikes = (GalleryLikeRepository); export const ModerationLogs = (ModerationLogRepository); export const Clips = (ClipRepository); export const ClipNotes = db.getRepository(ClipNote); diff --git a/packages/backend/src/models/repositories/gallery-like.ts b/packages/backend/src/models/repositories/gallery-like.ts deleted file mode 100644 index 33f5b3ebb..000000000 --- a/packages/backend/src/models/repositories/gallery-like.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { db } from '@/db/postgre.js'; -import { GalleryLike } from '@/models/entities/gallery-like.js'; -import { GalleryPosts } from '../index.js'; - -export const GalleryLikeRepository = db.getRepository(GalleryLike).extend({ - async pack( - src: GalleryLike['id'] | GalleryLike, - me?: any, - ) { - const like = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src }); - - return { - id: like.id, - post: await GalleryPosts.pack(like.post || like.postId, me), - }; - }, - - packMany( - likes: any[], - me: any, - ) { - return Promise.all(likes.map(x => this.pack(x, me))); - }, -}); diff --git a/packages/backend/src/models/repositories/gallery-post.ts b/packages/backend/src/models/repositories/gallery-post.ts deleted file mode 100644 index 7c54001ec..000000000 --- a/packages/backend/src/models/repositories/gallery-post.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { db } from '@/db/postgre.js'; -import { Packed } from '@/misc/schema.js'; -import { GalleryPost } from '@/models/entities/gallery-post.js'; -import { User } from '@/models/entities/user.js'; -import { awaitAll } from '@/prelude/await-all.js'; -import { Users, DriveFiles, GalleryLikes } from '../index.js'; - -export const GalleryPostRepository = db.getRepository(GalleryPost).extend({ - async pack( - src: GalleryPost['id'] | GalleryPost, - me?: { id: User['id'] } | null | undefined, - ): Promise> { - const meId = me ? me.id : null; - const post = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src }); - - return await awaitAll({ - id: post.id, - createdAt: post.createdAt.toISOString(), - updatedAt: post.updatedAt.toISOString(), - userId: post.userId, - user: Users.pack(post.user || post.userId, me), - title: post.title, - description: post.description, - fileIds: post.fileIds, - files: DriveFiles.packMany(post.fileIds), - tags: post.tags.length > 0 ? post.tags : undefined, - isSensitive: post.isSensitive, - likedCount: post.likedCount, - isLiked: meId ? await GalleryLikes.findOneBy({ postId: post.id, userId: meId }).then(x => x != null) : undefined, - }); - }, - - packMany( - posts: GalleryPost[], - me?: { id: User['id'] } | null | undefined, - ) { - return Promise.all(posts.map(x => this.pack(x, me))); - }, -}); diff --git a/packages/backend/src/models/schema/gallery-post.ts b/packages/backend/src/models/schema/gallery-post.ts deleted file mode 100644 index fc503d4a6..000000000 --- a/packages/backend/src/models/schema/gallery-post.ts +++ /dev/null @@ -1,69 +0,0 @@ -export const packedGalleryPostSchema = { - type: 'object', - properties: { - id: { - type: 'string', - optional: false, nullable: false, - format: 'id', - example: 'xxxxxxxxxx', - }, - createdAt: { - type: 'string', - optional: false, nullable: false, - format: 'date-time', - }, - updatedAt: { - type: 'string', - optional: false, nullable: false, - format: 'date-time', - }, - title: { - type: 'string', - optional: false, nullable: false, - }, - description: { - type: 'string', - optional: false, nullable: true, - }, - userId: { - type: 'string', - optional: false, nullable: false, - format: 'id', - }, - user: { - type: 'object', - ref: 'UserLite', - optional: false, nullable: false, - }, - fileIds: { - type: 'array', - optional: true, nullable: false, - items: { - type: 'string', - optional: false, nullable: false, - format: 'id', - }, - }, - files: { - type: 'array', - optional: true, nullable: false, - items: { - type: 'object', - optional: false, nullable: false, - ref: 'DriveFile', - }, - }, - tags: { - type: 'array', - optional: true, nullable: false, - items: { - type: 'string', - optional: false, nullable: false, - }, - }, - isSensitive: { - type: 'boolean', - optional: false, nullable: false, - }, - }, -} as const; diff --git a/packages/backend/src/server/api/endpoints.ts b/packages/backend/src/server/api/endpoints.ts index 59c4305c2..926d8d66f 100644 --- a/packages/backend/src/server/api/endpoints.ts +++ b/packages/backend/src/server/api/endpoints.ts @@ -138,15 +138,6 @@ import * as ep___following_requests_accept from './endpoints/following/requests/ import * as ep___following_requests_cancel from './endpoints/following/requests/cancel.js'; import * as ep___following_requests_list from './endpoints/following/requests/list.js'; import * as ep___following_requests_reject from './endpoints/following/requests/reject.js'; -import * as ep___gallery_featured from './endpoints/gallery/featured.js'; -import * as ep___gallery_popular from './endpoints/gallery/popular.js'; -import * as ep___gallery_posts from './endpoints/gallery/posts.js'; -import * as ep___gallery_posts_create from './endpoints/gallery/posts/create.js'; -import * as ep___gallery_posts_delete from './endpoints/gallery/posts/delete.js'; -import * as ep___gallery_posts_like from './endpoints/gallery/posts/like.js'; -import * as ep___gallery_posts_show from './endpoints/gallery/posts/show.js'; -import * as ep___gallery_posts_unlike from './endpoints/gallery/posts/unlike.js'; -import * as ep___gallery_posts_update from './endpoints/gallery/posts/update.js'; import * as ep___getOnlineUsersCount from './endpoints/get-online-users-count.js'; import * as ep___hashtags_list from './endpoints/hashtags/list.js'; import * as ep___hashtags_search from './endpoints/hashtags/search.js'; @@ -171,8 +162,6 @@ import * as ep___i_exportMute from './endpoints/i/export-mute.js'; import * as ep___i_exportNotes from './endpoints/i/export-notes.js'; import * as ep___i_exportUserLists from './endpoints/i/export-user-lists.js'; import * as ep___i_favorites from './endpoints/i/favorites.js'; -import * as ep___i_gallery_likes from './endpoints/i/gallery/likes.js'; -import * as ep___i_gallery_posts from './endpoints/i/gallery/posts.js'; import * as ep___i_getWordMutedNotesCount from './endpoints/i/get-word-muted-notes-count.js'; import * as ep___i_importBlocking from './endpoints/i/import-blocking.js'; import * as ep___i_importFollowing from './endpoints/i/import-following.js'; @@ -276,7 +265,6 @@ import * as ep___users from './endpoints/users.js'; import * as ep___users_clips from './endpoints/users/clips.js'; import * as ep___users_followers from './endpoints/users/followers.js'; import * as ep___users_following from './endpoints/users/following.js'; -import * as ep___users_gallery_posts from './endpoints/users/gallery/posts.js'; import * as ep___users_groups_create from './endpoints/users/groups/create.js'; import * as ep___users_groups_delete from './endpoints/users/groups/delete.js'; import * as ep___users_groups_invitations_accept from './endpoints/users/groups/invitations/accept.js'; @@ -446,15 +434,6 @@ const eps = [ ['following/requests/cancel', ep___following_requests_cancel], ['following/requests/list', ep___following_requests_list], ['following/requests/reject', ep___following_requests_reject], - ['gallery/featured', ep___gallery_featured], - ['gallery/popular', ep___gallery_popular], - ['gallery/posts', ep___gallery_posts], - ['gallery/posts/create', ep___gallery_posts_create], - ['gallery/posts/delete', ep___gallery_posts_delete], - ['gallery/posts/like', ep___gallery_posts_like], - ['gallery/posts/show', ep___gallery_posts_show], - ['gallery/posts/unlike', ep___gallery_posts_unlike], - ['gallery/posts/update', ep___gallery_posts_update], ['get-online-users-count', ep___getOnlineUsersCount], ['hashtags/list', ep___hashtags_list], ['hashtags/search', ep___hashtags_search], @@ -479,8 +458,6 @@ const eps = [ ['i/export-notes', ep___i_exportNotes], ['i/export-user-lists', ep___i_exportUserLists], ['i/favorites', ep___i_favorites], - ['i/gallery/likes', ep___i_gallery_likes], - ['i/gallery/posts', ep___i_gallery_posts], ['i/get-word-muted-notes-count', ep___i_getWordMutedNotesCount], ['i/import-blocking', ep___i_importBlocking], ['i/import-following', ep___i_importFollowing], @@ -584,7 +561,6 @@ const eps = [ ['users/clips', ep___users_clips], ['users/followers', ep___users_followers], ['users/following', ep___users_following], - ['users/gallery/posts', ep___users_gallery_posts], ['users/groups/create', ep___users_groups_create], ['users/groups/delete', ep___users_groups_delete], ['users/groups/invitations/accept', ep___users_groups_invitations_accept], diff --git a/packages/backend/src/server/api/endpoints/gallery/featured.ts b/packages/backend/src/server/api/endpoints/gallery/featured.ts deleted file mode 100644 index baa70c14e..000000000 --- a/packages/backend/src/server/api/endpoints/gallery/featured.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { DAY } from '@/const.js'; -import { GalleryPosts } from '@/models/index.js'; -import define from '../../define.js'; - -export const meta = { - tags: ['gallery'], - - requireCredential: false, - - res: { - type: 'array', - optional: false, nullable: false, - items: { - type: 'object', - optional: false, nullable: false, - ref: 'GalleryPost', - }, - }, -} as const; - -export const paramDef = { - type: 'object', - properties: {}, - required: [], -} as const; - -// eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, me) => { - const query = GalleryPosts.createQueryBuilder('post') - .andWhere('post.createdAt > :date', { date: new Date(Date.now() - 3 * DAY) }) - .andWhere('post.likedCount > 0') - .orderBy('post.likedCount', 'DESC'); - - const posts = await query.take(10).getMany(); - - return await GalleryPosts.packMany(posts, me); -}); diff --git a/packages/backend/src/server/api/endpoints/gallery/popular.ts b/packages/backend/src/server/api/endpoints/gallery/popular.ts deleted file mode 100644 index 552810e54..000000000 --- a/packages/backend/src/server/api/endpoints/gallery/popular.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { GalleryPosts } from '@/models/index.js'; -import define from '../../define.js'; - -export const meta = { - tags: ['gallery'], - - requireCredential: false, - - res: { - type: 'array', - optional: false, nullable: false, - items: { - type: 'object', - optional: false, nullable: false, - ref: 'GalleryPost', - }, - }, -} as const; - -export const paramDef = { - type: 'object', - properties: {}, - required: [], -} as const; - -// eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, me) => { - const query = GalleryPosts.createQueryBuilder('post') - .andWhere('post.likedCount > 0') - .orderBy('post.likedCount', 'DESC'); - - const posts = await query.take(10).getMany(); - - return await GalleryPosts.packMany(posts, me); -}); diff --git a/packages/backend/src/server/api/endpoints/gallery/posts.ts b/packages/backend/src/server/api/endpoints/gallery/posts.ts deleted file mode 100644 index 3a21afae1..000000000 --- a/packages/backend/src/server/api/endpoints/gallery/posts.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { GalleryPosts } from '@/models/index.js'; -import define from '../../define.js'; -import { makePaginationQuery } from '../../common/make-pagination-query.js'; - -export const meta = { - tags: ['gallery'], - - res: { - type: 'array', - optional: false, nullable: false, - items: { - type: 'object', - optional: false, nullable: false, - ref: 'GalleryPost', - }, - }, -} as const; - -export const paramDef = { - type: 'object', - properties: { - limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, - sinceId: { type: 'string', format: 'misskey:id' }, - untilId: { type: 'string', format: 'misskey:id' }, - }, - required: [], -} as const; - -// eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, me) => { - const query = makePaginationQuery(GalleryPosts.createQueryBuilder('post'), ps.sinceId, ps.untilId) - .innerJoinAndSelect('post.user', 'user'); - - const posts = await query.take(ps.limit).getMany(); - - return await GalleryPosts.packMany(posts, me); -}); diff --git a/packages/backend/src/server/api/endpoints/gallery/posts/create.ts b/packages/backend/src/server/api/endpoints/gallery/posts/create.ts deleted file mode 100644 index 230b7bec3..000000000 --- a/packages/backend/src/server/api/endpoints/gallery/posts/create.ts +++ /dev/null @@ -1,72 +0,0 @@ -import { DriveFiles, GalleryPosts } from '@/models/index.js'; -import { genId } from '@/misc/gen-id.js'; -import { GalleryPost } from '@/models/entities/gallery-post.js'; -import { DriveFile } from '@/models/entities/drive-file.js'; -import { HOUR } from '@/const.js'; -import { ApiError } from '@/server/api/error.js'; -import define from '../../../define.js'; - -export const meta = { - tags: ['gallery'], - - requireCredential: true, - - kind: 'write:gallery', - - limit: { - duration: HOUR, - max: 300, - }, - - res: { - type: 'object', - optional: false, nullable: false, - ref: 'GalleryPost', - }, -} as const; - -export const paramDef = { - type: 'object', - properties: { - title: { type: 'string', minLength: 1 }, - description: { type: 'string', nullable: true }, - fileIds: { type: 'array', uniqueItems: true, minItems: 1, maxItems: 32, items: { - type: 'string', format: 'misskey:id', - } }, - isSensitive: { type: 'boolean', default: false }, - }, - required: ['title', 'fileIds'], -} as const; - -// eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { - const files = (await Promise.all(ps.fileIds.map(fileId => - DriveFiles.findOneBy({ - id: fileId, - userId: user.id, - }), - ))).filter((file): file is DriveFile => file != null); - - if (files.length !== ps.fileIds.length) { - throw new ApiError( - 'INVALID_PARAM', - { - param: '#/properties/fileIds/items', - reason: 'contains invalid file IDs', - }, - ); - } - - const post = await GalleryPosts.insert(new GalleryPost({ - id: genId(), - createdAt: new Date(), - updatedAt: new Date(), - title: ps.title, - description: ps.description, - userId: user.id, - isSensitive: ps.isSensitive, - fileIds: files.map(file => file.id), - })).then(x => GalleryPosts.findOneByOrFail(x.identifiers[0])); - - return await GalleryPosts.pack(post, user); -}); diff --git a/packages/backend/src/server/api/endpoints/gallery/posts/delete.ts b/packages/backend/src/server/api/endpoints/gallery/posts/delete.ts deleted file mode 100644 index 65c0e62d0..000000000 --- a/packages/backend/src/server/api/endpoints/gallery/posts/delete.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { GalleryPosts } from '@/models/index.js'; -import define from '../../../define.js'; -import { ApiError } from '../../../error.js'; - -export const meta = { - tags: ['gallery'], - - requireCredential: true, - - kind: 'write:gallery', - - errors: ['NO_SUCH_POST'], -} as const; - -export const paramDef = { - type: 'object', - properties: { - postId: { type: 'string', format: 'misskey:id' }, - }, - required: ['postId'], -} as const; - -// eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { - const post = await GalleryPosts.findOneBy({ - id: ps.postId, - userId: user.id, - }); - - if (post == null) throw new ApiError('NO_SUCH_POST'); - - await GalleryPosts.delete(post.id); -}); diff --git a/packages/backend/src/server/api/endpoints/gallery/posts/like.ts b/packages/backend/src/server/api/endpoints/gallery/posts/like.ts deleted file mode 100644 index 1525d4dad..000000000 --- a/packages/backend/src/server/api/endpoints/gallery/posts/like.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { GalleryPosts, GalleryLikes } from '@/models/index.js'; -import { genId } from '@/misc/gen-id.js'; -import define from '../../../define.js'; -import { ApiError } from '../../../error.js'; - -export const meta = { - tags: ['gallery'], - - requireCredential: true, - - kind: 'write:gallery-likes', - - errors: ['NO_SUCH_POST', 'ALREADY_LIKED'], -} as const; - -export const paramDef = { - type: 'object', - properties: { - postId: { type: 'string', format: 'misskey:id' }, - }, - required: ['postId'], -} as const; - -// eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { - const post = await GalleryPosts.findOneBy({ id: ps.postId }); - if (post == null) throw new ApiError('NO_SUCH_POST'); - - // if already liked - const exist = await GalleryLikes.countBy({ - postId: post.id, - userId: user.id, - }); - - if (exist) throw new ApiError('ALREADY_LIKED'); - - // Create like - await GalleryLikes.insert({ - id: genId(), - createdAt: new Date(), - postId: post.id, - userId: user.id, - }); - - GalleryPosts.increment({ id: post.id }, 'likedCount', 1); -}); diff --git a/packages/backend/src/server/api/endpoints/gallery/posts/show.ts b/packages/backend/src/server/api/endpoints/gallery/posts/show.ts deleted file mode 100644 index 640048028..000000000 --- a/packages/backend/src/server/api/endpoints/gallery/posts/show.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { GalleryPosts } from '@/models/index.js'; -import define from '../../../define.js'; -import { ApiError } from '../../../error.js'; - -export const meta = { - tags: ['gallery'], - - requireCredential: false, - - errors: ['NO_SUCH_POST'], - - res: { - type: 'object', - optional: false, nullable: false, - ref: 'GalleryPost', - }, -} as const; - -export const paramDef = { - type: 'object', - properties: { - postId: { type: 'string', format: 'misskey:id' }, - }, - required: ['postId'], -} as const; - -// eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, me) => { - const post = await GalleryPosts.findOneBy({ - id: ps.postId, - }); - - if (post == null) throw new ApiError('NO_SUCH_POST'); - - return await GalleryPosts.pack(post, me); -}); diff --git a/packages/backend/src/server/api/endpoints/gallery/posts/unlike.ts b/packages/backend/src/server/api/endpoints/gallery/posts/unlike.ts deleted file mode 100644 index 61d71905e..000000000 --- a/packages/backend/src/server/api/endpoints/gallery/posts/unlike.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { GalleryPosts, GalleryLikes } from '@/models/index.js'; -import define from '../../../define.js'; -import { ApiError } from '../../../error.js'; - -export const meta = { - tags: ['gallery'], - - requireCredential: true, - - kind: 'write:gallery-likes', - - errors: ['NO_SUCH_POST', 'NOT_LIKED'], -} as const; - -export const paramDef = { - type: 'object', - properties: { - postId: { type: 'string', format: 'misskey:id' }, - }, - required: ['postId'], -} as const; - -// eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { - const post = await GalleryPosts.findOneBy({ id: ps.postId }); - if (post == null) throw new ApiError('NO_SUCH_POST'); - - const exist = await GalleryLikes.findOneBy({ - postId: post.id, - userId: user.id, - }); - - if (exist == null) throw new ApiError('NOT_LIKED'); - - // Delete like - await GalleryLikes.delete(exist.id); - - GalleryPosts.decrement({ id: post.id }, 'likedCount', 1); -}); diff --git a/packages/backend/src/server/api/endpoints/gallery/posts/update.ts b/packages/backend/src/server/api/endpoints/gallery/posts/update.ts deleted file mode 100644 index 071229f89..000000000 --- a/packages/backend/src/server/api/endpoints/gallery/posts/update.ts +++ /dev/null @@ -1,75 +0,0 @@ -import { DriveFiles, GalleryPosts } from '@/models/index.js'; -import { DriveFile } from '@/models/entities/drive-file.js'; -import { HOUR } from '@/const.js'; -import { ApiError } from '@/server/api/error.js'; -import define from '../../../define.js'; - -export const meta = { - tags: ['gallery'], - - requireCredential: true, - - kind: 'write:gallery', - - limit: { - duration: HOUR, - max: 300, - }, - - res: { - type: 'object', - optional: false, nullable: false, - ref: 'GalleryPost', - }, - - errors: ['INVALID_PARAM'], -} as const; - -export const paramDef = { - type: 'object', - properties: { - postId: { type: 'string', format: 'misskey:id' }, - title: { type: 'string', minLength: 1 }, - description: { type: 'string', nullable: true }, - fileIds: { type: 'array', uniqueItems: true, minItems: 1, maxItems: 32, items: { - type: 'string', format: 'misskey:id', - } }, - isSensitive: { type: 'boolean', default: false }, - }, - required: ['postId', 'title', 'fileIds'], -} as const; - -// eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { - const files = (await Promise.all(ps.fileIds.map(fileId => - DriveFiles.findOneBy({ - id: fileId, - userId: user.id, - }), - ))).filter((file): file is DriveFile => file != null); - - if (files.length !== ps.fileIds.length) { - throw new ApiError( - 'INVALID_PARAM', - { - param: '#/properties/fileIds/items', - reason: 'contains invalid file IDs', - }, - ); - } - - await GalleryPosts.update({ - id: ps.postId, - userId: user.id, - }, { - updatedAt: new Date(), - title: ps.title, - description: ps.description, - isSensitive: ps.isSensitive, - fileIds: files.map(file => file.id), - }); - - const post = await GalleryPosts.findOneByOrFail({ id: ps.postId }); - - return await GalleryPosts.pack(post, user); -}); diff --git a/packages/backend/src/server/api/endpoints/i/gallery/likes.ts b/packages/backend/src/server/api/endpoints/i/gallery/likes.ts deleted file mode 100644 index f5ceddd43..000000000 --- a/packages/backend/src/server/api/endpoints/i/gallery/likes.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { GalleryLikes } from '@/models/index.js'; -import define from '../../../define.js'; -import { makePaginationQuery } from '../../../common/make-pagination-query.js'; - -export const meta = { - tags: ['account', 'gallery'], - - requireCredential: true, - - kind: 'read:gallery-likes', - - res: { - type: 'array', - optional: false, nullable: false, - items: { - type: 'object', - optional: false, nullable: false, - properties: { - id: { - type: 'string', - optional: false, nullable: false, - format: 'id', - }, - post: { - type: 'object', - optional: false, nullable: false, - ref: 'GalleryPost', - }, - }, - }, - }, -} as const; - -export const paramDef = { - type: 'object', - properties: { - limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, - sinceId: { type: 'string', format: 'misskey:id' }, - untilId: { type: 'string', format: 'misskey:id' }, - }, - required: [], -} as const; - -// eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { - const query = makePaginationQuery(GalleryLikes.createQueryBuilder('like'), ps.sinceId, ps.untilId) - .andWhere('like.userId = :meId', { meId: user.id }) - .leftJoinAndSelect('like.post', 'post'); - - const likes = await query - .take(ps.limit) - .getMany(); - - return await GalleryLikes.packMany(likes, user); -}); diff --git a/packages/backend/src/server/api/endpoints/i/gallery/posts.ts b/packages/backend/src/server/api/endpoints/i/gallery/posts.ts deleted file mode 100644 index 2ecd47f1b..000000000 --- a/packages/backend/src/server/api/endpoints/i/gallery/posts.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { GalleryPosts } from '@/models/index.js'; -import define from '../../../define.js'; -import { makePaginationQuery } from '../../../common/make-pagination-query.js'; - -export const meta = { - tags: ['account', 'gallery'], - - requireCredential: true, - - kind: 'read:gallery', - - res: { - type: 'array', - optional: false, nullable: false, - items: { - type: 'object', - optional: false, nullable: false, - ref: 'GalleryPost', - }, - }, -} as const; - -export const paramDef = { - type: 'object', - properties: { - limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, - sinceId: { type: 'string', format: 'misskey:id' }, - untilId: { type: 'string', format: 'misskey:id' }, - }, - required: [], -} as const; - -// eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { - const query = makePaginationQuery(GalleryPosts.createQueryBuilder('post'), ps.sinceId, ps.untilId) - .andWhere('post.userId = :meId', { meId: user.id }); - - const posts = await query - .take(ps.limit) - .getMany(); - - return await GalleryPosts.packMany(posts, user); -}); diff --git a/packages/backend/src/server/api/endpoints/users/gallery/posts.ts b/packages/backend/src/server/api/endpoints/users/gallery/posts.ts deleted file mode 100644 index 8184c4ce4..000000000 --- a/packages/backend/src/server/api/endpoints/users/gallery/posts.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { GalleryPosts } from '@/models/index.js'; -import define from '../../../define.js'; -import { makePaginationQuery } from '../../../common/make-pagination-query.js'; - -export const meta = { - tags: ['users', 'gallery'], - - description: 'Show all gallery posts by the given user.', - - res: { - type: 'array', - optional: false, nullable: false, - items: { - type: 'object', - optional: false, nullable: false, - ref: 'GalleryPost', - }, - }, -} as const; - -export const paramDef = { - type: 'object', - properties: { - userId: { type: 'string', format: 'misskey:id' }, - limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, - sinceId: { type: 'string', format: 'misskey:id' }, - untilId: { type: 'string', format: 'misskey:id' }, - }, - required: ['userId'], -} as const; - -// eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { - const query = makePaginationQuery(GalleryPosts.createQueryBuilder('post'), ps.sinceId, ps.untilId) - .andWhere('post.userId = :userId', { userId: ps.userId }); - - const posts = await query - .take(ps.limit) - .getMany(); - - return await GalleryPosts.packMany(posts, user); -}); diff --git a/packages/backend/src/server/api/error.ts b/packages/backend/src/server/api/error.ts index c015c7608..a98354df9 100644 --- a/packages/backend/src/server/api/error.ts +++ b/packages/backend/src/server/api/error.ts @@ -73,7 +73,7 @@ export const errors: Record httpStatusCode: 409, }, ALREADY_LIKED: { - message: 'You already liked that page or gallery post.', + message: 'You already liked that page.', httpStatusCode: 409, }, ALREADY_MUTING: { @@ -292,10 +292,6 @@ export const errors: Record message: 'No such parent folder.', httpStatusCode: 404, }, - NO_SUCH_POST: { - message: 'No such gallery post.', - httpStatusCode: 404, - }, NO_SUCH_RESET_REQUEST: { message: 'No such password reset request.', httpStatusCode: 404, @@ -337,7 +333,7 @@ export const errors: Record httpStatusCode: 409, }, NOT_LIKED: { - message: 'You have not liked that page or gallery post.', + message: 'You have not liked that page.', httpStatusCode: 409, }, NOT_MUTING: { diff --git a/packages/backend/src/server/web/index.ts b/packages/backend/src/server/web/index.ts index 3776c24f2..d126d2650 100644 --- a/packages/backend/src/server/web/index.ts +++ b/packages/backend/src/server/web/index.ts @@ -18,7 +18,7 @@ import { KoaAdapter } from '@bull-board/koa'; import { In, IsNull } from 'typeorm'; import { fetchMeta } from '@/misc/fetch-meta.js'; import config from '@/config/index.js'; -import { Users, Notes, UserProfiles, Pages, Channels, Clips, GalleryPosts } from '@/models/index.js'; +import { Users, Notes, UserProfiles, Pages, Channels, Clips } from '@/models/index.js'; import * as Acct from '@/misc/acct.js'; import { getNoteSummary } from '@/misc/get-note-summary.js'; import { queues } from '@/queue/queues.js'; @@ -421,31 +421,6 @@ router.get('/clips/:clip', async (ctx, next) => { await next(); }); -// Gallery post -router.get('/gallery/:post', async (ctx, next) => { - const post = await GalleryPosts.findOneBy({ id: ctx.params.post }); - - if (post) { - const _post = await GalleryPosts.pack(post); - const profile = await UserProfiles.findOneByOrFail({ userId: post.userId }); - const meta = await fetchMeta(); - await ctx.render('gallery-post', { - post: _post, - profile, - avatarUrl: await Users.getAvatarUrl(await Users.findOneByOrFail({ id: post.userId })), - instanceName: meta.name || 'FoundKey', - icon: meta.iconUrl, - themeColor: meta.themeColor, - }); - - ctx.set('Cache-Control', 'public, max-age=15'); - - return; - } - - await next(); -}); - // Channel router.get('/channels/:channel', async (ctx, next) => { const channel = await Channels.findOneBy({ diff --git a/packages/backend/src/server/web/views/gallery-post.pug b/packages/backend/src/server/web/views/gallery-post.pug deleted file mode 100644 index ca0663a48..000000000 --- a/packages/backend/src/server/web/views/gallery-post.pug +++ /dev/null @@ -1,33 +0,0 @@ -extends ./base - -block vars - - const user = post.user; - - const title = post.title; - - const url = `${config.url}/gallery/${post.id}`; - -block title - = `${title} | ${instanceName}` - -block desc - meta(name='description' content= post.description) - -block og - meta(property='og:type' content='article') - meta(property='og:title' content= title) - meta(property='og:description' content= post.description) - meta(property='og:url' content= url) - meta(property='og:image' content= post.files[0].thumbnailUrl) - -block meta - if user.host || profile.noCrawle - meta(name='robots' content='noindex') - - meta(name='misskey:user-username' content=user.username) - meta(name='misskey:user-id' content=user.id) - - // todo - if user.twitter - meta(name='twitter:creator' content=`@${user.twitter.screenName}`) - - if !user.host - link(rel='alternate' href=url type='application/activity+json') From c4b5952788976cc50ac0281e8f1f21c3264ccbd1 Mon Sep 17 00:00:00 2001 From: Johann150 Date: Tue, 17 Jan 2023 21:17:00 +0100 Subject: [PATCH 07/46] migrate galleries to notes/clips --- .../migration/1673892262930-remove-groups.js | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 packages/backend/migration/1673892262930-remove-groups.js diff --git a/packages/backend/migration/1673892262930-remove-groups.js b/packages/backend/migration/1673892262930-remove-groups.js new file mode 100644 index 000000000..9779dd6e0 --- /dev/null +++ b/packages/backend/migration/1673892262930-remove-groups.js @@ -0,0 +1,65 @@ +import { genId } from '../built/misc/gen-id.js'; + +export class removeGroups1673892262930 { + name = 'removeGroups1673892262930'; + + async up(queryRunner) { + // migrate gallery posts into notes, keeping the ids + await queryRunner.query(` + INSERT INTO "note" ( + "id", "createdAt", "text", "cw", "userId", "visibility", "fileIds", "attachedFileTypes", "tags" + ) + WITH "file_types" ("id", "types") AS ( + SELECT "gallery_post"."id", ARRAY_AGG("drive_file"."type") + FROM "gallery_post" + JOIN "drive_file" ON "drive_file"."id" = ANY("gallery_post"."fileIds") + GROUP BY "gallery_post"."id" + ) + SELECT "gallery_post"."id", "gallery_post"."createdAt", + CASE + WHEN "gallery_post"."title" IS NULL THEN "gallery_post"."description" + ELSE '' || "gallery_post"."title" || E'\\n\\n' || "gallery_post"."description" + END, + CASE + WHEN "gallery_post"."isSensitive" THEN 'NSFW' + ELSE NULL + END, + "gallery_post"."userId", 'home', "gallery_post"."fileIds", "file_types"."types", "gallery_post"."tags" + FROM "gallery_post" + JOIN "file_types" ON "gallery_post"."id" = "file_types"."id" + `); + // make a clip for each users gallery + await queryRunner.query(`SELECT DISTINCT "userId" FROM "gallery_post"`).then(userIds => + Promise.all(userIds.map(({ userId }) => { + const clipId = genId(); + + // generate the clip itself + return queryRunner.query(`INSERT INTO "clip" ("id", "createdAt", "userId", "name", "isPublic") VALUES ($1, now(), $2, 'Gallery', true)`, [clipId, userId]) + // and add all the previous gallery posts to it + // to not have to use genId for each gallery post, we just prepend a zero, something that could never be generated by genId + .then(() => queryRunner.query(`INSERT INTO "clip_note" ("id", "noteId", "clipId") SELECT '0' || "id", "id", $1 FROM "gallery_post" WHERE "userId" = $2`, [clipId, userId])); + })) + ); + + await queryRunner.query(`DROP TABLE "gallery_like"`); + await queryRunner.query(`DROP TABLE "gallery_post"`); + } + + async down(queryRunner) { + // can only restore the table structure + await queryRunner.query(`CREATE TABLE "gallery_post" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL, "title" character varying(256) NOT NULL, "description" character varying(2048), "userId" character varying(32) NOT NULL, "fileIds" character varying(32) array NOT NULL DEFAULT '{}'::varchar[], "isSensitive" boolean NOT NULL DEFAULT false, "likedCount" integer NOT NULL DEFAULT '0', "tags" character varying(128) array NOT NULL DEFAULT '{}'::varchar[], CONSTRAINT "PK_8e90d7b6015f2c4518881b14753" PRIMARY KEY ("id")); COMMENT ON COLUMN "gallery_post"."createdAt" IS 'The created date of the GalleryPost.'; COMMENT ON COLUMN "gallery_post"."updatedAt" IS 'The updated date of the GalleryPost.'; COMMENT ON COLUMN "gallery_post"."userId" IS 'The ID of author.'; COMMENT ON COLUMN "gallery_post"."isSensitive" IS 'Whether the post is sensitive.'`); + await queryRunner.query(`CREATE INDEX "IDX_8f1a239bd077c8864a20c62c2c" ON "gallery_post" ("createdAt") `); + await queryRunner.query(`CREATE INDEX "IDX_f631d37835adb04792e361807c" ON "gallery_post" ("updatedAt") `); + await queryRunner.query(`CREATE INDEX "IDX_985b836dddd8615e432d7043dd" ON "gallery_post" ("userId") `); + await queryRunner.query(`CREATE INDEX "IDX_3ca50563facd913c425e7a89ee" ON "gallery_post" ("fileIds") `); + await queryRunner.query(`CREATE INDEX "IDX_f2d744d9a14d0dfb8b96cb7fc5" ON "gallery_post" ("isSensitive") `); + await queryRunner.query(`CREATE INDEX "IDX_1a165c68a49d08f11caffbd206" ON "gallery_post" ("likedCount") `); + await queryRunner.query(`CREATE INDEX "IDX_05cca34b985d1b8edc1d1e28df" ON "gallery_post" ("tags") `); + await queryRunner.query(`CREATE TABLE "gallery_like" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "userId" character varying(32) NOT NULL, "postId" character varying(32) NOT NULL, CONSTRAINT "PK_853ab02be39b8de45cd720cc15f" PRIMARY KEY ("id"))`); + await queryRunner.query(`CREATE INDEX "IDX_8fd5215095473061855ceb948c" ON "gallery_like" ("userId") `); + await queryRunner.query(`CREATE UNIQUE INDEX "IDX_df1b5f4099e99fb0bc5eae53b6" ON "gallery_like" ("userId", "postId") `); + await queryRunner.query(`ALTER TABLE "gallery_post" ADD CONSTRAINT "FK_985b836dddd8615e432d7043ddb" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); + await queryRunner.query(`ALTER TABLE "gallery_like" ADD CONSTRAINT "FK_8fd5215095473061855ceb948cf" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); + await queryRunner.query(`ALTER TABLE "gallery_like" ADD CONSTRAINT "FK_b1cb568bfe569e47b7051699fc8" FOREIGN KEY ("postId") REFERENCES "gallery_post"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); + } +} From c792e4199cc4d4bac59b42ef25ea5485861e3d90 Mon Sep 17 00:00:00 2001 From: Johann150 Date: Sun, 22 Jan 2023 21:42:49 +0100 Subject: [PATCH 08/46] server: add missing return in extractQuoteUrl --- packages/backend/src/remote/activitypub/models/tag.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/backend/src/remote/activitypub/models/tag.ts b/packages/backend/src/remote/activitypub/models/tag.ts index 182a23765..faa3984f8 100644 --- a/packages/backend/src/remote/activitypub/models/tag.ts +++ b/packages/backend/src/remote/activitypub/models/tag.ts @@ -45,5 +45,5 @@ export function extractQuoteUrl(tags: IObject | IObject[] | null | undefined): s // Deduplicate by href. // If there is more than one quote, we just pick the first/a random one. - quotes.filter((x, i, arr) => arr.findIndex(y => x.href === y.href) === i)[0].href; + return quotes.filter((x, i, arr) => arr.findIndex(y => x.href === y.href) === i)[0].href; } From 78f5ca3792fcb46a79a2edbf29da5e195ad190ab Mon Sep 17 00:00:00 2001 From: Johann150 Date: Sun, 22 Jan 2023 21:47:02 +0100 Subject: [PATCH 09/46] server: fix empty array in quote detection --- packages/backend/src/remote/activitypub/models/tag.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/backend/src/remote/activitypub/models/tag.ts b/packages/backend/src/remote/activitypub/models/tag.ts index faa3984f8..ed277fc7c 100644 --- a/packages/backend/src/remote/activitypub/models/tag.ts +++ b/packages/backend/src/remote/activitypub/models/tag.ts @@ -39,11 +39,11 @@ export function extractQuoteUrl(tags: IObject | IObject[] | null | undefined): s 'https://www.w3.org/ns/activitystreams#quoteUrl', ].includes(rel) ) - ); + ) + // Deduplicate by href. + .filter((x, i, arr) => arr.findIndex(y => x.href === y.href) === i); if (quotes.length === 0) return null; - - // Deduplicate by href. // If there is more than one quote, we just pick the first/a random one. - return quotes.filter((x, i, arr) => arr.findIndex(y => x.href === y.href) === i)[0].href; + else return quotes[0].href; } From 66ec8756240f84db5fc63818fa2c9a21603f9dd6 Mon Sep 17 00:00:00 2001 From: Johann150 Date: Mon, 23 Jan 2023 18:05:17 +0100 Subject: [PATCH 10/46] server: also search human readable URL Changelog: Fixed --- packages/backend/src/remote/activitypub/db-resolver.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/backend/src/remote/activitypub/db-resolver.ts b/packages/backend/src/remote/activitypub/db-resolver.ts index 7f913de0b..dc1b47fef 100644 --- a/packages/backend/src/remote/activitypub/db-resolver.ts +++ b/packages/backend/src/remote/activitypub/db-resolver.ts @@ -62,9 +62,11 @@ export class DbResolver { id: parsed.id, }); } else { - return await Notes.findOneBy({ + return await Notes.findOneBy([{ uri: parsed.uri, - }); + }, { + url: parsed.uri, + }]); } } From e7644eb7572d96534b02ed733eeb7fdc386f12b5 Mon Sep 17 00:00:00 2001 From: Johann150 Date: Mon, 23 Jan 2023 19:58:07 +0100 Subject: [PATCH 11/46] server: add index to human readable URL --- .../backend/migration/1674499888924-sync-orm.js | 17 +++++++++++++++++ .../backend/src/models/entities/access-token.ts | 1 - packages/backend/src/models/entities/note.ts | 1 + 3 files changed, 18 insertions(+), 1 deletion(-) create mode 100644 packages/backend/migration/1674499888924-sync-orm.js diff --git a/packages/backend/migration/1674499888924-sync-orm.js b/packages/backend/migration/1674499888924-sync-orm.js new file mode 100644 index 000000000..aac0d22e9 --- /dev/null +++ b/packages/backend/migration/1674499888924-sync-orm.js @@ -0,0 +1,17 @@ +export class syncOrm1674499888924 { + name = 'syncOrm1674499888924' + + async up(queryRunner) { + await queryRunner.query(`COMMENT ON COLUMN "user"."token" IS 'The native access token of local users, or null.'`); + await queryRunner.query(`ALTER TABLE "auth_session" DROP CONSTRAINT "UQ_8e001e5a101c6dca37df1a76d66"`); + await queryRunner.query(`CREATE UNIQUE INDEX "IDX_71d35fceee0d0fa62b2fa8f3b2" ON "note" ("url") `); + await queryRunner.query(`CREATE UNIQUE INDEX "IDX_d9ecaed8c6dc43f3592c229282" ON "user_group_joining" ("userId", "userGroupId") `); + } + + async down(queryRunner) { + await queryRunner.query(`DROP INDEX "public"."IDX_d9ecaed8c6dc43f3592c229282"`); + await queryRunner.query(`DROP INDEX "public"."IDX_71d35fceee0d0fa62b2fa8f3b2"`); + await queryRunner.query(`ALTER TABLE "auth_session" ADD CONSTRAINT "UQ_8e001e5a101c6dca37df1a76d66" UNIQUE ("accessTokenId")`); + await queryRunner.query(`COMMENT ON COLUMN "user"."token" IS 'The native access token of the User. It will be null if the origin of the user is local.'`); + } +} diff --git a/packages/backend/src/models/entities/access-token.ts b/packages/backend/src/models/entities/access-token.ts index b6dc8cebc..06a6cb00a 100644 --- a/packages/backend/src/models/entities/access-token.ts +++ b/packages/backend/src/models/entities/access-token.ts @@ -79,7 +79,6 @@ export class AccessToken { @Column('varchar', { length: 64, array: true, - default: '{}', }) public permission: string[]; diff --git a/packages/backend/src/models/entities/note.ts b/packages/backend/src/models/entities/note.ts index 1e5f418c5..e34dc53f7 100644 --- a/packages/backend/src/models/entities/note.ts +++ b/packages/backend/src/models/entities/note.ts @@ -117,6 +117,7 @@ export class Note { }) public uri: string | null; + @Index({ unique: true }) @Column('varchar', { length: 512, nullable: true, comment: 'The human readable url of a note. it will be null when the note is local.', From 21b20920c2c6dc28abbd7bf1358c548534a8fd97 Mon Sep 17 00:00:00 2001 From: Johann150 Date: Mon, 2 Jan 2023 13:06:25 +0100 Subject: [PATCH 12/46] docs: use endpoint stability to mark endpoints deprecated --- packages/backend/src/server/api/openapi/gen-spec.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/backend/src/server/api/openapi/gen-spec.ts b/packages/backend/src/server/api/openapi/gen-spec.ts index 3643887f7..74dfdac38 100644 --- a/packages/backend/src/server/api/openapi/gen-spec.ts +++ b/packages/backend/src/server/api/openapi/gen-spec.ts @@ -183,6 +183,7 @@ export function genOpenapiSpec() { }, }, responses, + deprecated: endpoint.meta.stability === 'deprecated', }; const path = { From 9b76c805ec5d109e5cdfc4097c8c268afa1d3b58 Mon Sep 17 00:00:00 2001 From: Johann150 Date: Wed, 25 Jan 2023 22:14:53 +0100 Subject: [PATCH 13/46] fix: DriveFile folder & user undefined instead of null when unrequested --- packages/backend/src/models/repositories/drive-file.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/backend/src/models/repositories/drive-file.ts b/packages/backend/src/models/repositories/drive-file.ts index f75207134..ce3e7b705 100644 --- a/packages/backend/src/models/repositories/drive-file.ts +++ b/packages/backend/src/models/repositories/drive-file.ts @@ -108,9 +108,9 @@ export const DriveFileRepository = db.getRepository(DriveFile).extend({ folderId: file.folderId, folder: opts.detail && file.folderId ? DriveFolders.pack(file.folderId, { detail: true, - }) : null, - userId: opts.withUser ? file.userId : null, - user: (opts.withUser && file.userId) ? Users.pack(file.userId) : null, + }) : undefined, + userId: file.userId, + user: (opts.withUser && file.userId) ? Users.pack(file.userId) : undefined, }); }, From 57cf6c71633c824ae168c7a5287a09dbfd018ea6 Mon Sep 17 00:00:00 2001 From: Johann150 Date: Thu, 26 Jan 2023 08:25:19 +0100 Subject: [PATCH 14/46] server: indicate Retry-After when rate limiting This refactors the rate limiting code to throw an ApiError directly. Changelog: Added --- packages/backend/src/server/api/call.ts | 6 ++--- packages/backend/src/server/api/error.ts | 12 ++++++++-- packages/backend/src/server/api/limiter.ts | 23 +++++++++++-------- .../backend/src/server/api/private/signin.ts | 9 ++------ 4 files changed, 27 insertions(+), 23 deletions(-) diff --git a/packages/backend/src/server/api/call.ts b/packages/backend/src/server/api/call.ts index 89493ce96..dc0e790bd 100644 --- a/packages/backend/src/server/api/call.ts +++ b/packages/backend/src/server/api/call.ts @@ -35,10 +35,8 @@ export default async (endpoint: string, user: CacheableLocalUser | null | undefi limit.key = ep.name; } - // Rate limit - await limiter(limit as IEndpointMeta['limit'] & { key: NonNullable }, limitActor).catch(() => { - throw new ApiError('RATE_LIMIT_EXCEEDED'); - }); + // Rate limit, may throw an ApiError + await limiter(limit as IEndpointMeta['limit'] & { key: NonNullable }, limitActor); } if (ep.meta.requireCredential && user == null) { diff --git a/packages/backend/src/server/api/error.ts b/packages/backend/src/server/api/error.ts index a98354df9..7636ab0ba 100644 --- a/packages/backend/src/server/api/error.ts +++ b/packages/backend/src/server/api/error.ts @@ -29,8 +29,16 @@ export class ApiError extends Error { */ public apply(ctx: Koa.Context, endpoint: string): void { ctx.status = this.httpStatusCode; - if (ctx.status === 401) { - ctx.response.set('WWW-Authenticate', 'Bearer'); + // set additional headers + switch (ctx.status) { + case 401: + ctx.response.set('WWW-Authenticate', 'Bearer'); + break; + case 429: + if (typeof this.info === 'object' && typeof this.info.reset === 'number') { + ctx.respose.set('Retry-After', Math.floor(this.info.reset - (Date.now() / 1000))); + } + break; } ctx.body = { error: { diff --git a/packages/backend/src/server/api/limiter.ts b/packages/backend/src/server/api/limiter.ts index 2b835d446..2780afff3 100644 --- a/packages/backend/src/server/api/limiter.ts +++ b/packages/backend/src/server/api/limiter.ts @@ -2,11 +2,12 @@ import Limiter from 'ratelimiter'; import Logger from '@/services/logger.js'; import { redisClient } from '@/db/redis.js'; import { IEndpointMeta } from './endpoints.js'; +import { ApiError } from './error.js'; const logger = new Logger('limiter'); -export const limiter = (limitation: IEndpointMeta['limit'] & { key: NonNullable }, actor: string) => new Promise((ok, reject) => { - if (process.env.NODE_ENV === 'test') ok(); +export const limiter = (limitation: IEndpointMeta['limit'] & { key: NonNullable }, actor: string) => new Promise((resolve) => { + if (process.env.NODE_ENV === 'test') resolve(); const hasShortTermLimit = typeof limitation.minInterval === 'number'; @@ -19,10 +20,10 @@ export const limiter = (limitation: IEndpointMeta['limit'] & { key: NonNullable< } else if (hasLongTermLimit) { max(); } else { - ok(); + resolve(); } - // Short-term limit + // Short-term limit, calls long term limit if appropriate. function min(): void { const minIntervalLimiter = new Limiter({ id: `${actor}:${limitation.key}:min`, @@ -33,18 +34,19 @@ export const limiter = (limitation: IEndpointMeta['limit'] & { key: NonNullable< minIntervalLimiter.get((err, info) => { if (err) { - return reject('ERR'); + logger.error(err); + throw new ApiError('INTERNAL_ERROR'); } logger.debug(`${actor} ${limitation.key} min remaining: ${info.remaining}`); if (info.remaining === 0) { - reject('BRIEF_REQUEST_INTERVAL'); + throw new ApiError('RATE_LIMIT_EXCEEDED', info); } else { if (hasLongTermLimit) { max(); } else { - ok(); + resolve(); } } }); @@ -61,15 +63,16 @@ export const limiter = (limitation: IEndpointMeta['limit'] & { key: NonNullable< limiter.get((err, info) => { if (err) { - return reject('ERR'); + logger.error(err); + throw new ApiError('INTERNAL_ERROR'); } logger.debug(`${actor} ${limitation.key} max remaining: ${info.remaining}`); if (info.remaining === 0) { - reject('RATE_LIMIT_EXCEEDED'); + throw new ApiError('RATE_LIMIT_EXCEEDED', info); } else { - ok(); + resolve(); } }); } diff --git a/packages/backend/src/server/api/private/signin.ts b/packages/backend/src/server/api/private/signin.ts index b2bfabe52..f910cb3c3 100644 --- a/packages/backend/src/server/api/private/signin.ts +++ b/packages/backend/src/server/api/private/signin.ts @@ -25,13 +25,8 @@ export default async (ctx: Koa.Context) => { new ApiError(e, info).apply(ctx, 'signin'); } - try { - // not more than 1 attempt per second and not more than 10 attempts per hour - await limiter({ key: 'signin', duration: HOUR, max: 10, minInterval: SECOND }, getIpHash(ctx.ip)); - } catch (err) { - error('RATE_LIMIT_EXCEEDED'); - return; - } + // not more than 1 attempt per second and not more than 10 attempts per hour + await limiter({ key: 'signin', duration: HOUR, max: 10, minInterval: SECOND }, getIpHash(ctx.ip)); if (typeof username !== 'string') { error('INVALID_PARAM', { param: 'username', reason: 'not a string' }); From 95a9027a6675142c641c2db5e297be95a9ea209c Mon Sep 17 00:00:00 2001 From: Johann150 Date: Thu, 26 Jan 2023 08:34:06 +0100 Subject: [PATCH 15/46] docs: show rate limit information Changelog: Added --- .../backend/src/server/api/openapi/gen-spec.ts | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/packages/backend/src/server/api/openapi/gen-spec.ts b/packages/backend/src/server/api/openapi/gen-spec.ts index 74dfdac38..04003097c 100644 --- a/packages/backend/src/server/api/openapi/gen-spec.ts +++ b/packages/backend/src/server/api/openapi/gen-spec.ts @@ -126,11 +126,21 @@ export function genOpenapiSpec() { }; } - let desc = (endpoint.meta.description ? endpoint.meta.description : 'No description provided.') + '\n\n'; + let desc = endpoint.meta.description ?? 'No description provided.'); desc += `**Credential required**: *${endpoint.meta.requireCredential ? 'Yes' : 'No'}*`; if (endpoint.meta.kind) { - const kind = endpoint.meta.kind; - desc += ` / **Permission**: *${kind}*`; + desc += `\n\n**Permission**: `' + endpoint.meta.kind + '`'; + } + if (endpoint.meta.limit) { + const limit = endpoint.meta.limit; + + desc += '\n### Rate limit\nRate limiting group: `' + (limit.key ?? endpoint.name) + '`'; + if (limit.duration && limit.max) { + desc += ` \nNo more than ${limit.max} requests every ${limit.duration} ms.`; + } + if (limit.minInterval) { + desc += ` \nMinimum delay between each request: ${endpoint.meta.limit.minInterval} ms.`; + } } const requestType = endpoint.meta.requireFile ? 'multipart/form-data' : 'application/json'; From 151053897ded058d88d83230a0c16e269b5163f7 Mon Sep 17 00:00:00 2001 From: Johann150 Date: Thu, 26 Jan 2023 08:35:42 +0100 Subject: [PATCH 16/46] server: lower rate limit for deletion activities Changelog: Changed --- packages/backend/src/server/api/endpoints/notes/delete.ts | 5 +++-- .../src/server/api/endpoints/notes/reactions/delete.ts | 5 +++-- packages/backend/src/server/api/endpoints/notes/unrenote.ts | 5 +++-- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/packages/backend/src/server/api/endpoints/notes/delete.ts b/packages/backend/src/server/api/endpoints/notes/delete.ts index 3d8af2c7c..f49faafaf 100644 --- a/packages/backend/src/server/api/endpoints/notes/delete.ts +++ b/packages/backend/src/server/api/endpoints/notes/delete.ts @@ -14,8 +14,9 @@ export const meta = { limit: { duration: HOUR, - max: 300, - minInterval: SECOND, + max: 30, + minInterval: 10 * SECOND, + key: 'delete', }, v2: { diff --git a/packages/backend/src/server/api/endpoints/notes/reactions/delete.ts b/packages/backend/src/server/api/endpoints/notes/reactions/delete.ts index b974fc3ef..51786b446 100644 --- a/packages/backend/src/server/api/endpoints/notes/reactions/delete.ts +++ b/packages/backend/src/server/api/endpoints/notes/reactions/delete.ts @@ -13,8 +13,9 @@ export const meta = { limit: { duration: HOUR, - max: 60, - minInterval: 3 * SECOND, + max: 30, + minInterval: 10 * SECOND, + key: 'delete', }, errors: ['NO_SUCH_NOTE', 'NOT_REACTED'], diff --git a/packages/backend/src/server/api/endpoints/notes/unrenote.ts b/packages/backend/src/server/api/endpoints/notes/unrenote.ts index 20f3aa429..71ff2a0c4 100644 --- a/packages/backend/src/server/api/endpoints/notes/unrenote.ts +++ b/packages/backend/src/server/api/endpoints/notes/unrenote.ts @@ -14,8 +14,9 @@ export const meta = { limit: { duration: HOUR, - max: 300, - minInterval: SECOND, + max: 30, + minInterval: 10 * SECOND, + key: 'delete', }, v2: { From 05f8172ce99a4c80c87946d77c0de584e1f98fb4 Mon Sep 17 00:00:00 2001 From: Johann150 Date: Thu, 26 Jan 2023 08:36:32 +0100 Subject: [PATCH 17/46] docs: describe /ap/ endpoints --- packages/backend/src/server/api/endpoints/ap/get.ts | 2 ++ packages/backend/src/server/api/endpoints/ap/show.ts | 2 ++ 2 files changed, 4 insertions(+) diff --git a/packages/backend/src/server/api/endpoints/ap/get.ts b/packages/backend/src/server/api/endpoints/ap/get.ts index 3a051086a..676b6141d 100644 --- a/packages/backend/src/server/api/endpoints/ap/get.ts +++ b/packages/backend/src/server/api/endpoints/ap/get.ts @@ -7,6 +7,8 @@ export const meta = { requireCredential: true, + description: 'Tries to fetch the given `uri` from the remote server.', + limit: { duration: HOUR, max: 30, diff --git a/packages/backend/src/server/api/endpoints/ap/show.ts b/packages/backend/src/server/api/endpoints/ap/show.ts index 414f71d31..4bf8f0091 100644 --- a/packages/backend/src/server/api/endpoints/ap/show.ts +++ b/packages/backend/src/server/api/endpoints/ap/show.ts @@ -18,6 +18,8 @@ export const meta = { requireCredential: true, + description: 'Shows the requested object. If necessary, fetches the object from the remote server.', + limit: { duration: HOUR, max: 30, From 36031c083a02346c9cf340d0b26d0ac43116f4c0 Mon Sep 17 00:00:00 2001 From: Johann150 Date: Thu, 26 Jan 2023 13:25:13 +0100 Subject: [PATCH 18/46] docs: adjust parameters for v2 methods other than POST --- packages/backend/src/server/api/endpoints.ts | 8 +++++ .../server/api/endpoints/notes/children.ts | 1 + .../src/server/api/endpoints/notes/clips.ts | 1 + .../api/endpoints/notes/conversation.ts | 1 + .../src/server/api/endpoints/notes/delete.ts | 1 + .../server/api/endpoints/notes/reactions.ts | 3 +- .../src/server/api/endpoints/notes/renotes.ts | 1 + .../src/server/api/endpoints/notes/replies.ts | 1 + .../src/server/api/endpoints/notes/show.ts | 1 + .../src/server/api/endpoints/notes/state.ts | 1 + .../server/api/endpoints/notes/translate.ts | 3 +- .../server/api/endpoints/notes/unrenote.ts | 1 + .../src/server/api/openapi/gen-spec.ts | 34 ++++++++++++++++--- 13 files changed, 51 insertions(+), 6 deletions(-) diff --git a/packages/backend/src/server/api/endpoints.ts b/packages/backend/src/server/api/endpoints.ts index 926d8d66f..02f13d3a9 100644 --- a/packages/backend/src/server/api/endpoints.ts +++ b/packages/backend/src/server/api/endpoints.ts @@ -693,6 +693,14 @@ export interface IEndpointMeta { * @example (v0) /api/notes/create -> /api/v2/notes */ readonly alias?: string; + + /** + * If any path parameters were used, they have to be listed here. + * Otherwise they will show up as query parameters in the documentation. + * + * Note: Path parameters cannot be optional. + */ + readonly pathParamers?: string[]; }; } diff --git a/packages/backend/src/server/api/endpoints/notes/children.ts b/packages/backend/src/server/api/endpoints/notes/children.ts index 091bab766..db270af3a 100644 --- a/packages/backend/src/server/api/endpoints/notes/children.ts +++ b/packages/backend/src/server/api/endpoints/notes/children.ts @@ -25,6 +25,7 @@ export const meta = { v2: { method: 'get', alias: 'notes/:noteId/children', + pathParameters: ['noteId'], }, } as const; diff --git a/packages/backend/src/server/api/endpoints/notes/clips.ts b/packages/backend/src/server/api/endpoints/notes/clips.ts index 3cd878800..e87daedcf 100644 --- a/packages/backend/src/server/api/endpoints/notes/clips.ts +++ b/packages/backend/src/server/api/endpoints/notes/clips.ts @@ -22,6 +22,7 @@ export const meta = { v2: { method: 'get', alias: 'notes/:noteId/clips', + pathParameters: ['noteId'], }, errors: ['NO_SUCH_NOTE'], diff --git a/packages/backend/src/server/api/endpoints/notes/conversation.ts b/packages/backend/src/server/api/endpoints/notes/conversation.ts index 7e736c736..3ea80f860 100644 --- a/packages/backend/src/server/api/endpoints/notes/conversation.ts +++ b/packages/backend/src/server/api/endpoints/notes/conversation.ts @@ -22,6 +22,7 @@ export const meta = { v2: { method: 'get', alias: 'notes/:noteId/conversation', + pathParameters: ['noteId'], }, errors: ['NO_SUCH_NOTE'], diff --git a/packages/backend/src/server/api/endpoints/notes/delete.ts b/packages/backend/src/server/api/endpoints/notes/delete.ts index f49faafaf..83f768c37 100644 --- a/packages/backend/src/server/api/endpoints/notes/delete.ts +++ b/packages/backend/src/server/api/endpoints/notes/delete.ts @@ -22,6 +22,7 @@ export const meta = { v2: { method: 'delete', alias: 'notes/:noteId', + pathParameters: ['noteId'], }, errors: ['ACCESS_DENIED', 'NO_SUCH_NOTE'], diff --git a/packages/backend/src/server/api/endpoints/notes/reactions.ts b/packages/backend/src/server/api/endpoints/notes/reactions.ts index 6fd0e8f16..2b73a381d 100644 --- a/packages/backend/src/server/api/endpoints/notes/reactions.ts +++ b/packages/backend/src/server/api/endpoints/notes/reactions.ts @@ -25,7 +25,8 @@ export const meta = { v2: { method: 'get', - alias: 'notes/:noteId/reactions/:type?', + alias: 'notes/:noteId/reactions', + pathParameters: ['noteId'], }, errors: ['NO_SUCH_NOTE'], diff --git a/packages/backend/src/server/api/endpoints/notes/renotes.ts b/packages/backend/src/server/api/endpoints/notes/renotes.ts index 55674f9ee..af02aa15c 100644 --- a/packages/backend/src/server/api/endpoints/notes/renotes.ts +++ b/packages/backend/src/server/api/endpoints/notes/renotes.ts @@ -25,6 +25,7 @@ export const meta = { v2: { method: 'get', alias: 'notes/:noteId/renotes', + pathParameters: ['noteId'], }, errors: ['NO_SUCH_NOTE'], diff --git a/packages/backend/src/server/api/endpoints/notes/replies.ts b/packages/backend/src/server/api/endpoints/notes/replies.ts index 34b679ae4..e3bb33047 100644 --- a/packages/backend/src/server/api/endpoints/notes/replies.ts +++ b/packages/backend/src/server/api/endpoints/notes/replies.ts @@ -25,6 +25,7 @@ export const meta = { v2: { method: 'get', alias: 'notes/:noteId/replies', + pathParameters: ['noteId'], }, errors: ['NO_SUCH_NOTE'], diff --git a/packages/backend/src/server/api/endpoints/notes/show.ts b/packages/backend/src/server/api/endpoints/notes/show.ts index 6c3f68407..9f3c68f32 100644 --- a/packages/backend/src/server/api/endpoints/notes/show.ts +++ b/packages/backend/src/server/api/endpoints/notes/show.ts @@ -17,6 +17,7 @@ export const meta = { v2: { method: 'get', alias: 'notes/:noteId', + pathParameters: ['noteId'], }, errors: ['NO_SUCH_NOTE'], diff --git a/packages/backend/src/server/api/endpoints/notes/state.ts b/packages/backend/src/server/api/endpoints/notes/state.ts index c578e1419..a98903177 100644 --- a/packages/backend/src/server/api/endpoints/notes/state.ts +++ b/packages/backend/src/server/api/endpoints/notes/state.ts @@ -30,6 +30,7 @@ export const meta = { v2: { method: 'get', alias: 'notes/:noteId/status', + pathParameters: ['noteId'], }, errors: ['NO_SUCH_NOTE'], diff --git a/packages/backend/src/server/api/endpoints/notes/translate.ts b/packages/backend/src/server/api/endpoints/notes/translate.ts index 1c4989354..e366f84dd 100644 --- a/packages/backend/src/server/api/endpoints/notes/translate.ts +++ b/packages/backend/src/server/api/endpoints/notes/translate.ts @@ -57,7 +57,8 @@ export const meta = { v2: { method: 'get', - alias: 'notes/:noteId/translate/:targetLang/:sourceLang?', + alias: 'notes/:noteId/translate/:targetLang', + pathParameters: ['noteId', 'targetLang'], }, errors: ['NO_SUCH_NOTE'], diff --git a/packages/backend/src/server/api/endpoints/notes/unrenote.ts b/packages/backend/src/server/api/endpoints/notes/unrenote.ts index 71ff2a0c4..446a14981 100644 --- a/packages/backend/src/server/api/endpoints/notes/unrenote.ts +++ b/packages/backend/src/server/api/endpoints/notes/unrenote.ts @@ -22,6 +22,7 @@ export const meta = { v2: { method: 'delete', alias: 'notes/:noteId/renotes', + pathParameters: ['noteId'], }, errors: ['NO_SUCH_NOTE'], diff --git a/packages/backend/src/server/api/openapi/gen-spec.ts b/packages/backend/src/server/api/openapi/gen-spec.ts index 04003097c..ece0bef24 100644 --- a/packages/backend/src/server/api/openapi/gen-spec.ts +++ b/packages/backend/src/server/api/openapi/gen-spec.ts @@ -126,10 +126,10 @@ export function genOpenapiSpec() { }; } - let desc = endpoint.meta.description ?? 'No description provided.'); + let desc = endpoint.meta.description ?? 'No description provided.'; desc += `**Credential required**: *${endpoint.meta.requireCredential ? 'Yes' : 'No'}*`; if (endpoint.meta.kind) { - desc += `\n\n**Permission**: `' + endpoint.meta.kind + '`'; + desc += '\n\n**Permission**: `' + endpoint.meta.kind + '`'; } if (endpoint.meta.limit) { const limit = endpoint.meta.limit; @@ -220,11 +220,37 @@ export function genOpenapiSpec() { spec.paths['/' + endpoint.name] = path; if (endpoint.meta.v2) { + const route = `/v2/${endpoint.meta.v2.alias ?? endpoint.name.replace(/-/g, '_')}`; // we need a clone of the API endpoint info because otherwise we change it by reference const infoClone = structuredClone(info); - const route = `/v2/${endpoint.meta.v2.alias ?? endpoint.name.replace(/-/g, '_')}`; + // fix the way parameters are passed + const hasBody = !(endpoint.meta.v2.method === 'get' || endpoint.meta.v2.method === 'delete'); + if (!hasBody) { + // these methods do not (usually) have a body + delete infoClone.requestBody; + infoClone.parameters = []; + for (const name in schema.properties) { + infoClone.parameters.push({ + name, + in: endpoint.meta.v2?.pathParameters?.includes(name) ? 'path' : 'query', + schema: schema.properties[name], + required: endpoint.meta.v2?.pathParameters?.includes(name) || schema.required?.includes(name) || false, + }); + } + } else if (endpoint.meta.v2.pathParameters) { + for (const name in endpoint.meta.v2.pathParameters) { + delete infoClone.requestBody.content[requestType].schema.properties[name]; + infoClone.parameters.push({ + name, + in: 'path', + schema: schema.properties[name], + required: true, + }); + } + } - infoClone['operationId'] = infoClone['summary'] = route; + infoClone['operationId'] = endpoint.meta.v2.method.toUpperCase() + '/' + route; + infoClone['summary'] = endpoint.meta.v2.method.toUpperCase() + ' ' + route; spec.paths[route] = { ...spec.paths[route], From 8abd3ebec700aac42fa91994277a2a24ab5a598a Mon Sep 17 00:00:00 2001 From: Johann150 Date: Sun, 29 Jan 2023 12:38:38 +0100 Subject: [PATCH 19/46] client: remove notification forwarding to service worker This was an interim measure, but now that push notifications are always enabled, this should not be necessary any more and the service worker should receive all notifications automatically. --- packages/client/src/ui/_common_/common.vue | 8 -------- packages/sw/src/sw.ts | 3 --- 2 files changed, 11 deletions(-) diff --git a/packages/client/src/ui/_common_/common.vue b/packages/client/src/ui/_common_/common.vue index 3cec9b388..c7121d9d0 100644 --- a/packages/client/src/ui/_common_/common.vue +++ b/packages/client/src/ui/_common_/common.vue @@ -33,14 +33,6 @@ const dev: Ref = ref(_DEV_); const onNotification = (notification: { type: string; id: any; }): void => { if ($i?.mutingNotificationTypes.includes(notification.type)) return; - // if push notifications are enabled there is no need to pass the notification along - if (!instance.enableServiceWorker) { - // service worker is not enabled or set up on the server, pass the notification along - navigator.serviceWorker.ready.then(registration => { - registration.active.postMessage({ type: 'notification', body: notification }); - }); - } - sound.play('notification'); }; diff --git a/packages/sw/src/sw.ts b/packages/sw/src/sw.ts index 490c40b30..10cfc69d3 100644 --- a/packages/sw/src/sw.ts +++ b/packages/sw/src/sw.ts @@ -164,9 +164,6 @@ self.addEventListener('message', (ev: ServiceWorkerGlobalScopeEventMap['message' case 'initialize': swLang.setLang(ev.data.lang); break; - case 'notification': - createNotification(ev.data); - break; } } })()); From 6fd80816fa7a45899facb0ff80c450405b301d71 Mon Sep 17 00:00:00 2001 From: Johann150 Date: Sun, 29 Jan 2023 14:29:58 +0100 Subject: [PATCH 20/46] client: remove unused property from MFM component --- packages/client/src/components/mfm.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/packages/client/src/components/mfm.ts b/packages/client/src/components/mfm.ts index 73b297ab1..466fcbdb0 100644 --- a/packages/client/src/components/mfm.ts +++ b/packages/client/src/components/mfm.ts @@ -31,10 +31,6 @@ export default defineComponent({ type: Object, default: null, }, - i: { - type: Object, - default: null, - }, customEmojis: { required: false, }, From bb3ec8bafe5d16eea061929271f72032c8aeb6f3 Mon Sep 17 00:00:00 2001 From: Chloe Kudryavtsev Date: Mon, 30 Jan 2023 14:59:24 +0100 Subject: [PATCH 21/46] Revert "server: fix user deletion race condition" This reverts commit cc83cbe523169bc749f719bc7b5869e511584c11, reversing changes made to 8abd3ebec700aac42fa91994277a2a24ab5a598a. This changeset contains: * multiple type errors * a foreign key incompatibility * breaks outgoing note federation (in at least two ways) --- .../1673201544000-deletion-progress.js | 18 ------------- packages/backend/src/models/entities/user.ts | 8 +++--- .../backend/src/models/repositories/user.ts | 2 +- packages/backend/src/queue/index.ts | 26 ++++--------------- .../queue/processors/system/check-expired.ts | 7 +---- packages/backend/src/queue/types.ts | 2 -- .../src/remote/activitypub/deliver-manager.ts | 18 +++++-------- .../remote/activitypub/kernel/delete/actor.ts | 2 +- .../api/endpoints/admin/accounts/delete.ts | 3 +-- .../server/api/endpoints/i/delete-account.ts | 2 +- .../backend/src/services/delete-account.ts | 2 +- packages/backend/src/services/suspend-user.ts | 5 +--- 12 files changed, 23 insertions(+), 72 deletions(-) delete mode 100644 packages/backend/migration/1673201544000-deletion-progress.js diff --git a/packages/backend/migration/1673201544000-deletion-progress.js b/packages/backend/migration/1673201544000-deletion-progress.js deleted file mode 100644 index 90aa5cbf8..000000000 --- a/packages/backend/migration/1673201544000-deletion-progress.js +++ /dev/null @@ -1,18 +0,0 @@ -export class deletionProgress1673201544000 { - name = 'deletionProgress1673201544000'; - - async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "user" RENAME COLUMN "isDeleted" TO "isDeletedOld"`); - await queryRunner.query(`ALTER TABLE "user" ADD "isDeleted" integer`); - await queryRunner.query(`UPDATE "user" SET "isDeleted" = CASE WHEN "host" IS NULL THEN -1 ELSE 0 END WHERE "isDeletedOld"`); - await queryRunner.query(`ALTER TABLE "user" DROP COLUMN "isDeletedOld"`); - } - - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "user" RENAME COLUMN "isDeleted" TO "isDeletedOld"`); - await queryRunner.query(`ALTER TABLE "user" ADD "isDeleted" boolean NOT NULL DEFAULT false`); - await queryRunner.query(`UPDATE "user" SET "isDeleted" = "isDeletedOld" IS NOT NULL`); - await queryRunner.query(`ALTER TABLE "user" DROP COLUMN "isDeletedOld"`); - } -} - diff --git a/packages/backend/src/models/entities/user.ts b/packages/backend/src/models/entities/user.ts index 374928fe1..0aee4c90b 100644 --- a/packages/backend/src/models/entities/user.ts +++ b/packages/backend/src/models/entities/user.ts @@ -163,11 +163,11 @@ export class User { // Indicates the user was deleted by an admin. // The users' data is not deleted from the database to keep them from reappearing. // A hard delete of the record may follow if we receive a matching Delete activity. - @Column('integer', { - nullable: true, - comment: 'How many delivery jobs are outstanding before the deletion is completed.', + @Column('boolean', { + default: false, + comment: 'Whether the User is deleted.', }) - public isDeleted: number | null; + public isDeleted: boolean; @Column('varchar', { length: 128, array: true, default: '{}', diff --git a/packages/backend/src/models/repositories/user.ts b/packages/backend/src/models/repositories/user.ts index 9598bcc5f..6a796b223 100644 --- a/packages/backend/src/models/repositories/user.ts +++ b/packages/backend/src/models/repositories/user.ts @@ -349,7 +349,7 @@ export const UserRepository = db.getRepository(User).extend({ autoAcceptFollowed: profile!.autoAcceptFollowed, noCrawle: profile!.noCrawle, isExplorable: user.isExplorable, - isDeleted: user.isDeleted != null, + isDeleted: user.isDeleted, hideOnlineStatus: user.hideOnlineStatus, hasUnreadSpecifiedNotes: NoteUnreads.count({ where: { userId: user.id, isSpecified: true }, diff --git a/packages/backend/src/queue/index.ts b/packages/backend/src/queue/index.ts index ab247209f..57de62a79 100644 --- a/packages/backend/src/queue/index.ts +++ b/packages/backend/src/queue/index.ts @@ -1,9 +1,7 @@ import httpSignature from '@peertube/http-signature'; import { v4 as uuid } from 'uuid'; -import Bull from 'bull'; import config from '@/config/index.js'; -import { Users } from '@/models/index.js'; import { DriveFile } from '@/models/entities/drive-file.js'; import { Webhook, webhookEventTypes } from '@/models/entities/webhook.js'; import { IActivity } from '@/remote/activitypub/type.js'; @@ -20,7 +18,7 @@ import { endedPollNotification } from './processors/ended-poll-notification.js'; import { queueLogger } from './logger.js'; import { getJobInfo } from './get-job-info.js'; import { systemQueue, dbQueue, deliverQueue, inboxQueue, objectStorageQueue, endedPollNotificationQueue, webhookDeliverQueue } from './queues.js'; -import { DeliverJobData, ThinUser } from './types.js'; +import { ThinUser } from './types.js'; function renderError(e: Error): any { return { @@ -37,12 +35,6 @@ const inboxLogger = queueLogger.createSubLogger('inbox'); const dbLogger = queueLogger.createSubLogger('db'); const objectStorageLogger = queueLogger.createSubLogger('objectStorage'); -async function deletionRefCount(job: Bull.Job): Promise { - if (job.data.deletingUserId) { - await Users.decrement({ id: job.data.deletingUserId }, 'isDeleted', 1); - } -} - systemQueue .on('waiting', (jobId) => systemLogger.debug(`waiting id=${jobId}`)) .on('active', (job) => systemLogger.debug(`active id=${job.id}`)) @@ -54,14 +46,8 @@ systemQueue deliverQueue .on('waiting', (jobId) => deliverLogger.debug(`waiting id=${jobId}`)) .on('active', (job) => deliverLogger.debug(`active ${getJobInfo(job, true)} to=${job.data.to}`)) - .on('completed', async (job, result) => { - deliverLogger.debug(`completed(${result}) ${getJobInfo(job, true)} to=${job.data.to}`); - await deletionRefCount(job); - }) - .on('failed', async (job, err) => { - deliverLogger.warn(`failed(${err}) ${getJobInfo(job)} to=${job.data.to}`); - await deletionRefCount(job); - }) + .on('completed', (job, result) => deliverLogger.debug(`completed(${result}) ${getJobInfo(job, true)} to=${job.data.to}`)) + .on('failed', (job, err) => deliverLogger.warn(`failed(${err}) ${getJobInfo(job)} to=${job.data.to}`)) .on('error', (job: any, err: Error) => deliverLogger.error(`error ${err}`, { job, e: renderError(err) })) .on('stalled', (job) => deliverLogger.warn(`stalled ${getJobInfo(job)} to=${job.data.to}`)); @@ -97,7 +83,7 @@ webhookDeliverQueue .on('error', (job: any, err: Error) => webhookLogger.error(`error ${err}`, { job, e: renderError(err) })) .on('stalled', (job) => webhookLogger.warn(`stalled ${getJobInfo(job)} to=${job.data.to}`)); -export function deliver(user: ThinUser, content: unknown, to: string | null, deletingUserId?: string) { +export function deliver(user: ThinUser, content: unknown, to: string | null) { if (content == null) return null; if (to == null) return null; @@ -107,7 +93,6 @@ export function deliver(user: ThinUser, content: unknown, to: string | null, del }, content, to, - deletingUserId, }; return deliverQueue.add(data, { @@ -341,9 +326,8 @@ export default function() { } export function destroy() { - deliverQueue.once('cleaned', async (jobs, status) => { + deliverQueue.once('cleaned', (jobs, status) => { deliverLogger.succ(`Cleaned ${jobs.length} ${status} jobs`); - await Promise.all(jobs.map(job => deletionRefCount(job)); }); deliverQueue.clean(0, 'delayed'); diff --git a/packages/backend/src/queue/processors/system/check-expired.ts b/packages/backend/src/queue/processors/system/check-expired.ts index 71bd498e9..eeb6149bb 100644 --- a/packages/backend/src/queue/processors/system/check-expired.ts +++ b/packages/backend/src/queue/processors/system/check-expired.ts @@ -1,6 +1,6 @@ import Bull from 'bull'; import { In, LessThan } from 'typeorm'; -import { AttestationChallenges, AuthSessions, Mutings, Notifications, PasswordResetRequests, Signins, Users } from '@/models/index.js'; +import { AttestationChallenges, AuthSessions, Mutings, Notifications, PasswordResetRequests, Signins } from '@/models/index.js'; import { publishUserEvent } from '@/services/stream.js'; import { MINUTE, MONTH } from '@/const.js'; import { queueLogger } from '@/queue/logger.js'; @@ -52,11 +52,6 @@ export async function checkExpired(job: Bull.Job>, done: createdAt: OlderThan(3 * MONTH), }); - await Users.delete({ - // delete users where the deletion status reference count has come down to zero - isDeleted: 0, - }); - logger.succ('Deleted expired data.'); done(); diff --git a/packages/backend/src/queue/types.ts b/packages/backend/src/queue/types.ts index d745a1fc7..82bd28703 100644 --- a/packages/backend/src/queue/types.ts +++ b/packages/backend/src/queue/types.ts @@ -12,8 +12,6 @@ export type DeliverJobData = { content: unknown; /** inbox URL to deliver */ to: string; - /** set if this job is part of a user deletion, on completion or failure the isDeleted field needs to be decremented */ - deletingUserId?: string; }; export type InboxJobData = { diff --git a/packages/backend/src/remote/activitypub/deliver-manager.ts b/packages/backend/src/remote/activitypub/deliver-manager.ts index dfadef150..4bc651c98 100644 --- a/packages/backend/src/remote/activitypub/deliver-manager.ts +++ b/packages/backend/src/remote/activitypub/deliver-manager.ts @@ -88,10 +88,10 @@ export class DeliverManager { /** * Execute delivers */ - public async execute(deletingUserId?: string) { + public async execute() { if (!Users.isLocalUser(this.actor)) return; - let inboxes = new Set(); + const inboxes = new Set(); /* build inbox list @@ -150,17 +150,13 @@ export class DeliverManager { )), ); - inboxes = inboxes.entries() - .filter(inbox => !instancesToSkip.includes(new URL(inbox).host)); + // deliver + for (const inbox of inboxes) { + // skip instances as indicated + if (instancesToSkip.includes(new URL(inbox).host)) continue; - if (deletingUserId) { - await Users.update(deletingUserId, { - // set deletion job count for reference counting before queueing jobs - isDeleted: inboxes.length, - }); + deliver(this.actor, this.activity, inbox); } - - inboxes.forEach(inbox => deliver(this.actor, this.activity, inbox, deletingUserId)); } } diff --git a/packages/backend/src/remote/activitypub/kernel/delete/actor.ts b/packages/backend/src/remote/activitypub/kernel/delete/actor.ts index f69d87d2c..ea75a9739 100644 --- a/packages/backend/src/remote/activitypub/kernel/delete/actor.ts +++ b/packages/backend/src/remote/activitypub/kernel/delete/actor.ts @@ -16,7 +16,7 @@ export async function deleteActor(actor: CacheableRemoteUser, uri: string): Prom // anyway, the user is gone now so dont care return 'ok: gone'; } - if (user.isDeleted != null) { + if (user.isDeleted) { // the actual deletion already happened by an admin, just delete the record await Users.delete(actor.id); } else { diff --git a/packages/backend/src/server/api/endpoints/admin/accounts/delete.ts b/packages/backend/src/server/api/endpoints/admin/accounts/delete.ts index 88065f81f..4f5ea5568 100644 --- a/packages/backend/src/server/api/endpoints/admin/accounts/delete.ts +++ b/packages/backend/src/server/api/endpoints/admin/accounts/delete.ts @@ -1,4 +1,3 @@ -import { IsNull } from 'typeorm'; import { Users } from '@/models/index.js'; import { ApiError } from '@/server/api/error.js'; import { deleteAccount } from '@/services/delete-account.js'; @@ -25,7 +24,7 @@ export const paramDef = { export default define(meta, paramDef, async (ps) => { const user = await Users.findOneBy({ id: ps.userId, - isDeleted: IsNull(), + isDeleted: false, }); if (user == null) { diff --git a/packages/backend/src/server/api/endpoints/i/delete-account.ts b/packages/backend/src/server/api/endpoints/i/delete-account.ts index a77afc8be..5dae620ae 100644 --- a/packages/backend/src/server/api/endpoints/i/delete-account.ts +++ b/packages/backend/src/server/api/endpoints/i/delete-account.ts @@ -27,7 +27,7 @@ export default define(meta, paramDef, async (ps, user) => { Users.findOneByOrFail({ id: user.id }), ]); - if (userDetailed.isDeleted != null) { + if (userDetailed.isDeleted) { return; } diff --git a/packages/backend/src/services/delete-account.ts b/packages/backend/src/services/delete-account.ts index bf95b2bc0..9c8d4c277 100644 --- a/packages/backend/src/services/delete-account.ts +++ b/packages/backend/src/services/delete-account.ts @@ -8,7 +8,7 @@ export async function deleteAccount(user: { host: string | null; }): Promise { await Users.update(user.id, { - isDeleted: -1, + isDeleted: true, }); if (Users.isLocalUser(user)) { diff --git a/packages/backend/src/services/suspend-user.ts b/packages/backend/src/services/suspend-user.ts index 7ed6bff80..11e6266a0 100644 --- a/packages/backend/src/services/suspend-user.ts +++ b/packages/backend/src/services/suspend-user.ts @@ -6,9 +6,6 @@ import { User } from '@/models/entities/user.js'; import { Users } from '@/models/index.js'; import { publishInternalEvent } from '@/services/stream.js'; -/** - * Sends an internal event and for local users queues the delete activites. - */ export async function doPostSuspend(user: { id: User['id']; host: User['host'] }): Promise { publishInternalEvent('userChangeSuspendedState', { id: user.id, isSuspended: true }); @@ -18,6 +15,6 @@ export async function doPostSuspend(user: { id: User['id']; host: User['host'] } // deliver to all of known network const dm = new DeliverManager(user, content); dm.addEveryone(); - await dm.execute(user.id); + await dm.execute(); } } From 2d32bc33d746bac9968d302e997437de00c791b8 Mon Sep 17 00:00:00 2001 From: Johann150 Date: Mon, 30 Jan 2023 19:23:12 +0100 Subject: [PATCH 22/46] server: fix error for invalid URLs in profile fields Co-authored-by: Chloe Kudryavtsev --- .../src/remote/activitypub/renderer/person.ts | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/packages/backend/src/remote/activitypub/renderer/person.ts b/packages/backend/src/remote/activitypub/renderer/person.ts index 7de957882..68f813aa8 100644 --- a/packages/backend/src/remote/activitypub/renderer/person.ts +++ b/packages/backend/src/remote/activitypub/renderer/person.ts @@ -30,12 +30,21 @@ export async function renderPerson(user: ILocalUser) { if (profile.fields) { for (const field of profile.fields) { + let value = field.value; + // try to parse it as a url + try { + if (field.value?.match(/^https?:/)) { + const url = new URL(field.value); + value = `${url.href}`; + } + } catch { + // guess it wasn't a url after all... + } + attachment.push({ type: 'PropertyValue', name: field.name, - value: (field.value != null && field.value.match(/^https?:/)) - ? `${new URL(field.value).href}` - : field.value, + value, }); } } From 953de3e4b2ee7f27ca221ff6b52d78a90b39ed5c Mon Sep 17 00:00:00 2001 From: Johann150 Date: Mon, 30 Jan 2023 19:36:22 +0100 Subject: [PATCH 23/46] adjust mailmap --- .mailmap | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.mailmap b/.mailmap index 4ae08d92f..51a2eb9dd 100644 --- a/.mailmap +++ b/.mailmap @@ -1,9 +1,9 @@ Andreas Nedbal Andreas Nedbal Balazs Nadasdi -Chloe Kudryavtsev -Chloe Kudryavtsev -Chloe Kudryavtsev +Chloe Kudryavtsev +Chloe Kudryavtsev +Chloe Kudryavtsev Dr. Gutfuck LLC <40531868+gutfuckllc@users.noreply.github.com> Ehsan Javadynia <31900907+ehsanjavadynia@users.noreply.github.com> Francis Dinh From 7480e27c0c3a08ba0af941d77ed56f398b6d067e Mon Sep 17 00:00:00 2001 From: Johann150 Date: Wed, 1 Feb 2023 11:27:27 +0100 Subject: [PATCH 24/46] server: remove twitter links from HTML templates Since the twitter integration has been removed, this will never be true and can therefore be removed. --- packages/backend/src/server/web/views/clip.pug | 4 ---- packages/backend/src/server/web/views/note.pug | 4 ---- packages/backend/src/server/web/views/page.pug | 4 ---- 3 files changed, 12 deletions(-) diff --git a/packages/backend/src/server/web/views/clip.pug b/packages/backend/src/server/web/views/clip.pug index 4c692bf59..7ad666fd9 100644 --- a/packages/backend/src/server/web/views/clip.pug +++ b/packages/backend/src/server/web/views/clip.pug @@ -25,7 +25,3 @@ block meta meta(name='misskey:user-username' content=user.username) meta(name='misskey:user-id' content=user.id) meta(name='misskey:clip-id' content=clip.id) - - // todo - if user.twitter - meta(name='twitter:creator' content=`@${user.twitter.screenName}`) diff --git a/packages/backend/src/server/web/views/note.pug b/packages/backend/src/server/web/views/note.pug index 65696ea13..37e8f8a41 100644 --- a/packages/backend/src/server/web/views/note.pug +++ b/packages/backend/src/server/web/views/note.pug @@ -27,10 +27,6 @@ block meta meta(name='misskey:user-id' content=user.id) meta(name='misskey:note-id' content=note.id) - // todo - if user.twitter - meta(name='twitter:creator' content=`@${user.twitter.screenName}`) - if note.prev link(rel='prev' href=`${config.url}/notes/${note.prev}`) if note.next diff --git a/packages/backend/src/server/web/views/page.pug b/packages/backend/src/server/web/views/page.pug index 4219e76a5..03af5cb86 100644 --- a/packages/backend/src/server/web/views/page.pug +++ b/packages/backend/src/server/web/views/page.pug @@ -25,7 +25,3 @@ block meta meta(name='misskey:user-username' content=user.username) meta(name='misskey:user-id' content=user.id) meta(name='misskey:page-id' content=page.id) - - // todo - if user.twitter - meta(name='twitter:creator' content=`@${user.twitter.screenName}`) From 75b14124f2d1ca6dc56ed6393f866b9240aa79fc Mon Sep 17 00:00:00 2001 From: Johann150 Date: Wed, 1 Feb 2023 11:30:53 +0100 Subject: [PATCH 25/46] server: improve variable naming --- packages/backend/src/server/web/index.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/backend/src/server/web/index.ts b/packages/backend/src/server/web/index.ts index d126d2650..85ad59f3a 100644 --- a/packages/backend/src/server/web/index.ts +++ b/packages/backend/src/server/web/index.ts @@ -324,15 +324,15 @@ router.get('/notes/:note', async (ctx, next) => { if (note) { try { // FIXME: packing with detail may throw an error if the reply or renote is not visible (#8774) - const _note = await Notes.pack(note); + const packedNote = await Notes.pack(note); const profile = await UserProfiles.findOneByOrFail({ userId: note.userId }); const meta = await fetchMeta(); await ctx.render('note', { - note: _note, + note: packedNote, profile, avatarUrl: await Users.getAvatarUrl(await Users.findOneByOrFail({ id: note.userId })), // TODO: Let locale changeable by instance setting - summary: getNoteSummary(_note), + summary: getNoteSummary(packedNote), instanceName: meta.name || 'FoundKey', icon: meta.iconUrl, themeColor: meta.themeColor, From 39fb7e594624957ec2e0f15ec2ec6332901587af Mon Sep 17 00:00:00 2001 From: Johann150 Date: Wed, 1 Feb 2023 11:34:09 +0100 Subject: [PATCH 26/46] server: improve OpenGraph data for note attachments With this change, not all files will be proclaimed to be image files. Only images, videos and audio files will be represented with OpenGraph data. More properties for these files will also be represented, e.g. image alt text. However, if the note has a CW or any of the files are marked sensitive, none of the files will be used. The users profile picture will not be used any more. Changelog: Changed --- packages/backend/src/server/web/index.ts | 64 ++++++++++++++++++- .../backend/src/server/web/views/note.pug | 3 +- 2 files changed, 64 insertions(+), 3 deletions(-) diff --git a/packages/backend/src/server/web/index.ts b/packages/backend/src/server/web/index.ts index 85ad59f3a..4d1726421 100644 --- a/packages/backend/src/server/web/index.ts +++ b/packages/backend/src/server/web/index.ts @@ -18,7 +18,7 @@ import { KoaAdapter } from '@bull-board/koa'; import { In, IsNull } from 'typeorm'; import { fetchMeta } from '@/misc/fetch-meta.js'; import config from '@/config/index.js'; -import { Users, Notes, UserProfiles, Pages, Channels, Clips } from '@/models/index.js'; +import { Users, Notes, UserProfiles, Pages, Channels, Clips, DriveFiles } from '@/models/index.js'; import * as Acct from '@/misc/acct.js'; import { getNoteSummary } from '@/misc/get-note-summary.js'; import { queues } from '@/queue/queues.js'; @@ -327,10 +327,70 @@ router.get('/notes/:note', async (ctx, next) => { const packedNote = await Notes.pack(note); const profile = await UserProfiles.findOneByOrFail({ userId: note.userId }); const meta = await fetchMeta(); + + // If the note has a CW (is sensitive as a whole) or any of the files is sensitive or there are no + // files, they are not used for a preview. + let filesOpengraph = []; + if (!packedNote.cw || packedNote.files.length > 0 || packedNote.files.all(file => !file.isSensitive)) { + let limit = 4; + for (const file of packedNote.files) { + if (file.type.startsWith('image/')) { + filesOpengraph.push([ + "og:image", + DriveFiles.getPublicUrl(file, true), + ]); + filesOpengraph.push([ + "og:image:type", + file.type, + ]); + if (file.properties != null) { + filesOpengraph.push([ + "og:image:width", + file.properties?.width, + ]); + filesOpengraph.push([ + "og:image:height", + file.properties?.height, + ]); + } + if (file.comment) { + filesOpengraph.push([ + "og:image:alt", + file.comment, + ]); + } + } else if (file.type.startsWith('audio/')) { + filesOpengraph.push([ + "og:audio", + DriveFiles.getPublicUrl(file), + ]); + filesOpengraph.push([ + "og:audio:type", + file.type, + ]); + } else if (file.type.startsWith('video/')) { + filesOpengraph.push([ + "og:video", + DriveFiles.getPublicUrl(file), + ]); + filesOpengraph.push([ + "og:video:type", + file.type, + ]); + } else { + // doesn't count towards the limit + continue; + } + + // limit the number of presented attachments + if (--limit < 0) break; + } + } + await ctx.render('note', { note: packedNote, profile, - avatarUrl: await Users.getAvatarUrl(await Users.findOneByOrFail({ id: note.userId })), + filesOpengraph, // TODO: Let locale changeable by instance setting summary: getNoteSummary(packedNote), instanceName: meta.name || 'FoundKey', diff --git a/packages/backend/src/server/web/views/note.pug b/packages/backend/src/server/web/views/note.pug index 37e8f8a41..0d9f50d89 100644 --- a/packages/backend/src/server/web/views/note.pug +++ b/packages/backend/src/server/web/views/note.pug @@ -17,7 +17,8 @@ block og meta(property='og:title' content= title) meta(property='og:description' content= summary) meta(property='og:url' content= url) - meta(property='og:image' content= avatarUrl) + for opengraphTag in filesOpengraph + meta(property=opengraphTag[0] content=opengraphTag[1]) block meta if user.host || isRenote || profile.noCrawle From be30e70344cb2862b4397ce0a5927dfda0014c2d Mon Sep 17 00:00:00 2001 From: Johann150 Date: Wed, 1 Feb 2023 23:18:10 +0100 Subject: [PATCH 27/46] server: add more OpenGraph data, remove custom misskey meta tags Changelog: Changed --- packages/backend/src/server/web/views/clip.pug | 14 +++++--------- packages/backend/src/server/web/views/note.pug | 12 +++++------- packages/backend/src/server/web/views/page.pug | 15 ++++++--------- packages/backend/src/server/web/views/user.pug | 11 ++++------- 4 files changed, 20 insertions(+), 32 deletions(-) diff --git a/packages/backend/src/server/web/views/clip.pug b/packages/backend/src/server/web/views/clip.pug index 7ad666fd9..12e402616 100644 --- a/packages/backend/src/server/web/views/clip.pug +++ b/packages/backend/src/server/web/views/clip.pug @@ -12,16 +12,12 @@ block desc meta(name='description' content= clip.description) block og - meta(property='og:type' content='article') - meta(property='og:title' content= title) - meta(property='og:description' content= clip.description) - meta(property='og:url' content= url) - meta(property='og:image' content= avatarUrl) + meta(property='og:type' content='website') + meta(property='og:title' content=title) + meta(property='og:description' content=clip.description) + meta(property='og:url' content=url) + meta(property='og:image' content=avatarUrl) block meta if profile.noCrawle meta(name='robots' content='noindex') - - meta(name='misskey:user-username' content=user.username) - meta(name='misskey:user-id' content=user.id) - meta(name='misskey:clip-id' content=clip.id) diff --git a/packages/backend/src/server/web/views/note.pug b/packages/backend/src/server/web/views/note.pug index 0d9f50d89..ae5cc0363 100644 --- a/packages/backend/src/server/web/views/note.pug +++ b/packages/backend/src/server/web/views/note.pug @@ -13,10 +13,12 @@ block desc meta(name='description' content= summary) block og - meta(property='og:type' content='article') - meta(property='og:title' content= title) + meta(property='og:type' content='article') + meta(property='og:article:published_time' content=note.createdAt.toISOString()) + meta(property='og:article:author:username' content=user.username) + meta(property='og:title' content= title) meta(property='og:description' content= summary) - meta(property='og:url' content= url) + meta(property='og:url' content= url) for opengraphTag in filesOpengraph meta(property=opengraphTag[0] content=opengraphTag[1]) @@ -24,10 +26,6 @@ block meta if user.host || isRenote || profile.noCrawle meta(name='robots' content='noindex') - meta(name='misskey:user-username' content=user.username) - meta(name='misskey:user-id' content=user.id) - meta(name='misskey:note-id' content=note.id) - if note.prev link(rel='prev' href=`${config.url}/notes/${note.prev}`) if note.next diff --git a/packages/backend/src/server/web/views/page.pug b/packages/backend/src/server/web/views/page.pug index 03af5cb86..54859e53e 100644 --- a/packages/backend/src/server/web/views/page.pug +++ b/packages/backend/src/server/web/views/page.pug @@ -12,16 +12,13 @@ block desc meta(name='description' content= page.summary) block og - meta(property='og:type' content='article') - meta(property='og:title' content= title) - meta(property='og:description' content= page.summary) - meta(property='og:url' content= url) - meta(property='og:image' content= page.eyeCatchingImage ? page.eyeCatchingImage.thumbnailUrl : avatarUrl) + meta(property='og:type' content='article') + meta(property='og:article:author:username' content=user.username) + meta(property='og:title' content=title) + meta(property='og:description' content=page.summary) + meta(property='og:url' content=url) + meta(property='og:image' content=page.eyeCatchingImage ? page.eyeCatchingImage.thumbnailUrl : avatarUrl) block meta if profile.noCrawle meta(name='robots' content='noindex') - - meta(name='misskey:user-username' content=user.username) - meta(name='misskey:user-id' content=user.id) - meta(name='misskey:page-id' content=page.id) diff --git a/packages/backend/src/server/web/views/user.pug b/packages/backend/src/server/web/views/user.pug index 119993fdb..dfcae89ec 100644 --- a/packages/backend/src/server/web/views/user.pug +++ b/packages/backend/src/server/web/views/user.pug @@ -11,19 +11,16 @@ block desc meta(name='description' content= profile.description) block og - meta(property='og:type' content='blog') - meta(property='og:title' content= title) + meta(property='og:type' content='profile') + meta(property='og:profile:username' content=user.username) meta(property='og:description' content= profile.description) - meta(property='og:url' content= url) - meta(property='og:image' content= avatarUrl) + meta(property='og:url' content=url) + meta(property='og:image' content=avatarUrl) block meta if user.host || profile.noCrawle meta(name='robots' content='noindex') - meta(name='misskey:user-username' content=user.username) - meta(name='misskey:user-id' content=user.id) - if profile.twitter meta(name='twitter:creator' content=`@${profile.twitter.screenName}`) From 8b98c9f2f4d3c6f077b6e448d30c4b566cfec0e8 Mon Sep 17 00:00:00 2001 From: Johann150 Date: Thu, 2 Feb 2023 23:24:26 +0100 Subject: [PATCH 28/46] server: remove unused 'domain' column --- packages/backend/src/models/entities/registry-item.ts | 7 ------- .../backend/src/server/api/endpoints/i/registry/get-all.ts | 1 - .../src/server/api/endpoints/i/registry/get-detail.ts | 1 - .../backend/src/server/api/endpoints/i/registry/get.ts | 1 - .../src/server/api/endpoints/i/registry/keys-with-type.ts | 1 - .../backend/src/server/api/endpoints/i/registry/keys.ts | 1 - .../backend/src/server/api/endpoints/i/registry/remove.ts | 1 - .../backend/src/server/api/endpoints/i/registry/scopes.ts | 1 - .../backend/src/server/api/endpoints/i/registry/set.ts | 2 -- 9 files changed, 16 deletions(-) diff --git a/packages/backend/src/models/entities/registry-item.ts b/packages/backend/src/models/entities/registry-item.ts index bbafae4a3..e82acb0e4 100644 --- a/packages/backend/src/models/entities/registry-item.ts +++ b/packages/backend/src/models/entities/registry-item.ts @@ -48,11 +48,4 @@ export class RegistryItem { length: 1024, array: true, default: '{}', }) public scope: string[]; - - // サードパーティアプリに開放するときのためのカラム - @Index() - @Column('varchar', { - length: 512, nullable: true, - }) - public domain: string | null; } diff --git a/packages/backend/src/server/api/endpoints/i/registry/get-all.ts b/packages/backend/src/server/api/endpoints/i/registry/get-all.ts index d92bc7af9..21fda2607 100644 --- a/packages/backend/src/server/api/endpoints/i/registry/get-all.ts +++ b/packages/backend/src/server/api/endpoints/i/registry/get-all.ts @@ -20,7 +20,6 @@ export const paramDef = { // eslint-disable-next-line import/no-default-export export default define(meta, paramDef, async (ps, user) => { const query = RegistryItems.createQueryBuilder('item') - .where('item.domain IS NULL') .andWhere('item.userId = :userId', { userId: user.id }) .andWhere('item.scope = :scope', { scope: ps.scope }); diff --git a/packages/backend/src/server/api/endpoints/i/registry/get-detail.ts b/packages/backend/src/server/api/endpoints/i/registry/get-detail.ts index 7addd0fab..2c3f7887f 100644 --- a/packages/backend/src/server/api/endpoints/i/registry/get-detail.ts +++ b/packages/backend/src/server/api/endpoints/i/registry/get-detail.ts @@ -24,7 +24,6 @@ export const paramDef = { // eslint-disable-next-line import/no-default-export export default define(meta, paramDef, async (ps, user) => { const query = RegistryItems.createQueryBuilder('item') - .where('item.domain IS NULL') .andWhere('item.userId = :userId', { userId: user.id }) .andWhere('item.key = :key', { key: ps.key }) .andWhere('item.scope = :scope', { scope: ps.scope }); diff --git a/packages/backend/src/server/api/endpoints/i/registry/get.ts b/packages/backend/src/server/api/endpoints/i/registry/get.ts index a9ca60485..02e31e626 100644 --- a/packages/backend/src/server/api/endpoints/i/registry/get.ts +++ b/packages/backend/src/server/api/endpoints/i/registry/get.ts @@ -24,7 +24,6 @@ export const paramDef = { // eslint-disable-next-line import/no-default-export export default define(meta, paramDef, async (ps, user) => { const query = RegistryItems.createQueryBuilder('item') - .where('item.domain IS NULL') .andWhere('item.userId = :userId', { userId: user.id }) .andWhere('item.key = :key', { key: ps.key }) .andWhere('item.scope = :scope', { scope: ps.scope }); diff --git a/packages/backend/src/server/api/endpoints/i/registry/keys-with-type.ts b/packages/backend/src/server/api/endpoints/i/registry/keys-with-type.ts index d9982b036..e865ce1db 100644 --- a/packages/backend/src/server/api/endpoints/i/registry/keys-with-type.ts +++ b/packages/backend/src/server/api/endpoints/i/registry/keys-with-type.ts @@ -20,7 +20,6 @@ export const paramDef = { // eslint-disable-next-line import/no-default-export export default define(meta, paramDef, async (ps, user) => { const query = RegistryItems.createQueryBuilder('item') - .where('item.domain IS NULL') .andWhere('item.userId = :userId', { userId: user.id }) .andWhere('item.scope = :scope', { scope: ps.scope }); diff --git a/packages/backend/src/server/api/endpoints/i/registry/keys.ts b/packages/backend/src/server/api/endpoints/i/registry/keys.ts index b625f50ca..3072805bc 100644 --- a/packages/backend/src/server/api/endpoints/i/registry/keys.ts +++ b/packages/backend/src/server/api/endpoints/i/registry/keys.ts @@ -21,7 +21,6 @@ export const paramDef = { export default define(meta, paramDef, async (ps, user) => { const query = RegistryItems.createQueryBuilder('item') .select('item.key') - .where('item.domain IS NULL') .andWhere('item.userId = :userId', { userId: user.id }) .andWhere('item.scope = :scope', { scope: ps.scope }); diff --git a/packages/backend/src/server/api/endpoints/i/registry/remove.ts b/packages/backend/src/server/api/endpoints/i/registry/remove.ts index f656f3d83..b75fbaff6 100644 --- a/packages/backend/src/server/api/endpoints/i/registry/remove.ts +++ b/packages/backend/src/server/api/endpoints/i/registry/remove.ts @@ -24,7 +24,6 @@ export const paramDef = { // eslint-disable-next-line import/no-default-export export default define(meta, paramDef, async (ps, user) => { const query = RegistryItems.createQueryBuilder('item') - .where('item.domain IS NULL') .andWhere('item.userId = :userId', { userId: user.id }) .andWhere('item.key = :key', { key: ps.key }) .andWhere('item.scope = :scope', { scope: ps.scope }); diff --git a/packages/backend/src/server/api/endpoints/i/registry/scopes.ts b/packages/backend/src/server/api/endpoints/i/registry/scopes.ts index d946fe609..9b0f43d33 100644 --- a/packages/backend/src/server/api/endpoints/i/registry/scopes.ts +++ b/packages/backend/src/server/api/endpoints/i/registry/scopes.ts @@ -17,7 +17,6 @@ export const paramDef = { export default define(meta, paramDef, async (ps, user) => { const query = RegistryItems.createQueryBuilder('item') .select('item.scope') - .where('item.domain IS NULL') .andWhere('item.userId = :userId', { userId: user.id }); const items = await query.getMany(); diff --git a/packages/backend/src/server/api/endpoints/i/registry/set.ts b/packages/backend/src/server/api/endpoints/i/registry/set.ts index 03d81d8c4..98eab9b35 100644 --- a/packages/backend/src/server/api/endpoints/i/registry/set.ts +++ b/packages/backend/src/server/api/endpoints/i/registry/set.ts @@ -24,7 +24,6 @@ export const paramDef = { // eslint-disable-next-line import/no-default-export export default define(meta, paramDef, async (ps, user) => { const query = RegistryItems.createQueryBuilder('item') - .where('item.domain IS NULL') .andWhere('item.userId = :userId', { userId: user.id }) .andWhere('item.key = :key', { key: ps.key }) .andWhere('item.scope = :scope', { scope: ps.scope }); @@ -42,7 +41,6 @@ export default define(meta, paramDef, async (ps, user) => { createdAt: new Date(), updatedAt: new Date(), userId: user.id, - domain: null, scope: ps.scope, key: ps.key, value: ps.value, From 17324e1e94f0f3d0d34fd6fd87a8a4d011b620db Mon Sep 17 00:00:00 2001 From: Johann150 Date: Fri, 3 Feb 2023 00:27:33 +0100 Subject: [PATCH 29/46] server: add unique constraint for registry items fixes https://akkoma.dev/FoundKeyGang/FoundKey/issues/335 --- .../1675375940759-registry-remove-domain.js | 19 +++++++++++++++++++ .../src/models/entities/registry-item.ts | 7 +++---- 2 files changed, 22 insertions(+), 4 deletions(-) create mode 100644 packages/backend/migration/1675375940759-registry-remove-domain.js diff --git a/packages/backend/migration/1675375940759-registry-remove-domain.js b/packages/backend/migration/1675375940759-registry-remove-domain.js new file mode 100644 index 000000000..50a6f6b8b --- /dev/null +++ b/packages/backend/migration/1675375940759-registry-remove-domain.js @@ -0,0 +1,19 @@ +export class registryRemoveDomain1675375940759 { + name = 'registryRemoveDomain1675375940759' + + async up(queryRunner) { + await queryRunner.query(`DROP INDEX "public"."IDX_0a72bdfcdb97c0eca11fe7ecad"`); + await queryRunner.query(`ALTER TABLE "registry_item" DROP COLUMN "domain"`); + await queryRunner.query(`ALTER TABLE "registry_item" ALTER COLUMN "key" TYPE text USING "key"::text`); + // delete existing duplicated entries, keeping the latest updated one + await queryRunner.query(`DELETE FROM "registry_item" AS "a" WHERE "updatedAt" != (SELECT MAX("updatedAt") OVER (PARTITION BY "userId", "key", "scope") FROM "registry_item" AS "b" WHERE "a"."userId" = "b"."userId" AND "a"."key" = "b"."key" AND "a"."scope" = "b"."scope")`); + await queryRunner.query(`ALTER TABLE "registry_item" ADD CONSTRAINT "UQ_b8d6509f847331273ab99daccc7" UNIQUE ("userId", "key", "scope")`); + } + + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "registry_item" DROP CONSTRAINT "UQ_b8d6509f847331273ab99daccc7"`); + await queryRunner.query(`ALTER TABLE "registry_item" ALTER COLUMN "key" TYPE character varying(1024) USING "key"::varchar(1024)`); + await queryRunner.query(`ALTER TABLE "registry_item" ADD "domain" character varying(512)`); + await queryRunner.query(`CREATE INDEX "IDX_0a72bdfcdb97c0eca11fe7ecad" ON "registry_item" ("domain") `); + } +} diff --git a/packages/backend/src/models/entities/registry-item.ts b/packages/backend/src/models/entities/registry-item.ts index e82acb0e4..9ed2af720 100644 --- a/packages/backend/src/models/entities/registry-item.ts +++ b/packages/backend/src/models/entities/registry-item.ts @@ -1,9 +1,9 @@ -import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; +import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne, Unique } from 'typeorm'; import { id } from '../id.js'; import { User } from './user.js'; -// TODO: 同じdomain、同じscope、同じkeyのレコードは二つ以上存在しないように制約付けたい @Entity() +@Unique(['userId', 'key', 'scope']) export class RegistryItem { @PrimaryColumn(id()) public id: string; @@ -31,8 +31,7 @@ export class RegistryItem { @JoinColumn() public user: User | null; - @Column('varchar', { - length: 1024, + @Column('text', { comment: 'The key of the RegistryItem.', }) public key: string; From 30c26abde7bdd76d497da5fc7149452586240c3a Mon Sep 17 00:00:00 2001 From: Johann150 Date: Fri, 3 Feb 2023 11:47:54 +0100 Subject: [PATCH 30/46] server: add websocket ping mechanism fixes https://akkoma.dev/FoundKeyGang/FoundKey/issues/336 Changelog: Fixed --- packages/backend/src/server/api/streaming.ts | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/packages/backend/src/server/api/streaming.ts b/packages/backend/src/server/api/streaming.ts index 825896b56..2be442f8f 100644 --- a/packages/backend/src/server/api/streaming.ts +++ b/packages/backend/src/server/api/streaming.ts @@ -2,7 +2,7 @@ import { EventEmitter } from 'events'; import * as http from 'node:http'; import { WebSocketServer } from 'ws'; -import { MINUTE } from '@/const.js'; +import { SECOND, MINUTE } from '@/const.js'; import { subscriber as redisClient } from '@/db/redis.js'; import { Users } from '@/models/index.js'; import { Connection } from './stream/index.js'; @@ -43,6 +43,20 @@ export const initializeStreamingServer = (server: http.Server): void => { const main = new Connection(socket, ev, user, app); + // ping/pong mechanism + let pingTimeout = null; + function startHeartbeat() { + if (pingTimeout) clearTimeout(pingTimeout); + + socket.ping(); + pingTimeout = setTimeout(() => { + socket.terminate(); + }, 30 * SECOND); + } + startHeartbeat(); + socket.on('ping', () => { startHeartbeat(); }); + socket.on('pong', () => { startHeartbeat(); }); + // keep user "online" while a stream is connected const intervalId = user ? setInterval(() => { Users.update(user.id, { @@ -54,11 +68,13 @@ export const initializeStreamingServer = (server: http.Server): void => { lastActiveDate: new Date(), }); } + socket.once('close', () => { ev.removeAllListeners(); main.dispose(); redisClient.off('message', onRedisMessage); if (intervalId) clearInterval(intervalId); + if (pingTimeout) clearTimeout(pingTimeout); }); // ping/pong mechanism From ca257d7d0c283b99b6a0c95cec6905862c3ae679 Mon Sep 17 00:00:00 2001 From: Johann150 Date: Fri, 3 Feb 2023 11:48:46 +0100 Subject: [PATCH 31/46] server: remove application level websocket ping Changelog: Removed --- packages/backend/src/server/api/streaming.ts | 8 -------- 1 file changed, 8 deletions(-) diff --git a/packages/backend/src/server/api/streaming.ts b/packages/backend/src/server/api/streaming.ts index 2be442f8f..797443afb 100644 --- a/packages/backend/src/server/api/streaming.ts +++ b/packages/backend/src/server/api/streaming.ts @@ -76,14 +76,6 @@ export const initializeStreamingServer = (server: http.Server): void => { if (intervalId) clearInterval(intervalId); if (pingTimeout) clearTimeout(pingTimeout); }); - - // ping/pong mechanism - // TODO: the websocket protocol already specifies a ping/pong mechanism, why is this necessary? - socket.on('message', async (data) => { - if (data.type === 'utf8' && data.utf8Data === 'ping') { - socket.send('pong'); - } - }); }); }); }; From a45908c1cbfb5049c8593f68c2bf52299d0cc58f Mon Sep 17 00:00:00 2001 From: Johann150 Date: Fri, 3 Feb 2023 23:12:12 +0100 Subject: [PATCH 32/46] client: check quoteId for canPost computation fixes https://akkoma.dev/FoundKeyGang/FoundKey/issues/334 Changelog: Fixed --- packages/client/src/components/post-form.vue | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/client/src/components/post-form.vue b/packages/client/src/components/post-form.vue index d929d8d32..3d5434096 100644 --- a/packages/client/src/components/post-form.vue +++ b/packages/client/src/components/post-form.vue @@ -207,7 +207,7 @@ const maxTextLength = $computed((): number => { const canPost = $computed((): boolean => { return !posting && - (1 <= textLength || 1 <= files.length || !!poll || !!props.renote) && + (1 <= textLength || 1 <= files.length || !!poll || !!props.renote || !!quoteId) && (textLength <= maxTextLength) && (!poll || poll.choices.length >= 2); }); @@ -573,7 +573,7 @@ async function post() { text: text === '' ? undefined : text, fileIds: files.length > 0 ? files.map(f => f.id) : undefined, replyId: props.reply ? props.reply.id : undefined, - renoteId: props.renote ? props.renote.id : quoteId ? quoteId : undefined, + renoteId: props.renote?.id ?? quoteId ?? undefined, channelId: props.channel ? props.channel.id : undefined, poll, cw: useCw ? cw || '' : undefined, From 0bb4a6af50bad03a8a26737572c668253af27c49 Mon Sep 17 00:00:00 2001 From: Johann150 Date: Fri, 3 Feb 2023 23:29:31 +0100 Subject: [PATCH 33/46] client: fix quotes with only a CW Changelog: Fixed --- packages/client/src/components/cw-button.vue | 1 + packages/client/src/components/note.vue | 1 + 2 files changed, 2 insertions(+) diff --git a/packages/client/src/components/cw-button.vue b/packages/client/src/components/cw-button.vue index 17299586c..c162f759c 100644 --- a/packages/client/src/components/cw-button.vue +++ b/packages/client/src/components/cw-button.vue @@ -26,6 +26,7 @@ const label = computed(() => { props.note.text ? [i18n.t('_cw.chars', { count: length(props.note.text) })] : [], props.note.files && props.note.files.length !== 0 ? [i18n.t('_cw.files', { count: props.note.files.length }) ] : [], props.note.poll != null ? [i18n.ts.poll] : [], + props.note.renoteId != null ? [i18n.ts.quote] : [], ] as string[][]).join(' / '); }); diff --git a/packages/client/src/components/note.vue b/packages/client/src/components/note.vue index f818f7dae..e8758329a 100644 --- a/packages/client/src/components/note.vue +++ b/packages/client/src/components/note.vue @@ -151,6 +151,7 @@ if (noteViewInterruptors.length > 0) { const isRenote = ( note.renote != null && note.text == null && + note.cw == null && note.fileIds.length === 0 && note.poll == null ); From 85a68a5eee14b8e638d2f9fabacf5ea3f2298cdf Mon Sep 17 00:00:00 2001 From: Johann150 Date: Sat, 4 Feb 2023 00:13:43 +0100 Subject: [PATCH 34/46] activitypub: properly render CW only quotes Changelog: Fixed --- packages/backend/src/misc/renote.ts | 3 ++- packages/backend/src/server/activitypub/outbox.ts | 2 +- packages/backend/src/services/note/create.ts | 15 +++------------ 3 files changed, 6 insertions(+), 14 deletions(-) diff --git a/packages/backend/src/misc/renote.ts b/packages/backend/src/misc/renote.ts index cd51cd04a..5b2657633 100644 --- a/packages/backend/src/misc/renote.ts +++ b/packages/backend/src/misc/renote.ts @@ -1,8 +1,9 @@ import { Note } from '@/models/entities/note.js'; -export function isPureRenote(note: Note): note is Note & { renoteId: string, text: null, fileIds: null | never[], hasPoll: false } { +export function isPureRenote(note: Note): note is Note & { renoteId: string, text: null, cw: null, fileIds: null | never[], hasPoll: false } { return note.renoteId != null && note.text == null + && note.cw == null && ( note.fileIds == null || note.fileIds.length === 0 diff --git a/packages/backend/src/server/activitypub/outbox.ts b/packages/backend/src/server/activitypub/outbox.ts index 60e2ab711..d517ffcfe 100644 --- a/packages/backend/src/server/activitypub/outbox.ts +++ b/packages/backend/src/server/activitypub/outbox.ts @@ -102,7 +102,7 @@ export default async (ctx: Router.RouterContext) => { export async function packActivity(note: Note): Promise { if (isPureRenote(note)) { const renote = await Notes.findOneByOrFail({ id: note.renoteId }); - return renderAnnounce(renote.uri ? renote.uri : `${config.url}/notes/${renote.id}`, note); + return renderAnnounce(renote.uri ?? `${config.url}/notes/${renote.id}`, note); } else { return renderCreate(await renderNote(note, false), note); } diff --git a/packages/backend/src/services/note/create.ts b/packages/backend/src/services/note/create.ts index 67f5e0d57..04b4a22a5 100644 --- a/packages/backend/src/services/note/create.ts +++ b/packages/backend/src/services/note/create.ts @@ -36,6 +36,7 @@ import { Cache } from '@/misc/cache.js'; import { UserProfile } from '@/models/entities/user-profile.js'; import { getActiveWebhooks } from '@/misc/webhook-cache.js'; import { IActivity } from '@/remote/activitypub/type.js'; +import { packActivity } from '@/server/activitypub/outbox.js'; import { MINUTE } from '@/const.js'; import { updateHashtags } from '../update-hashtag.js'; import { registerOrFetchInstanceDoc } from '../register-or-fetch-instance-doc.js'; @@ -428,9 +429,9 @@ export default async (user: { id: User['id']; username: User['username']; host: }); //#region AP deliver - if (Users.isLocalUser(user)) { + if (Users.isLocalUser(user) && !data.localOnly) { (async () => { - const noteActivity = await renderNoteOrRenoteActivity(data, note); + const noteActivity = renderActivity(await packActivity(note)); const dm = new DeliverManager(user, noteActivity); // Delivered to remote users who have been mentioned @@ -487,16 +488,6 @@ export default async (user: { id: User['id']; username: User['username']; host: index(note); }); -async function renderNoteOrRenoteActivity(data: Option, note: Note): Promise { - if (data.localOnly) return null; - - const content = data.renote && data.text == null && data.poll == null && (data.files == null || data.files.length === 0) - ? renderAnnounce(data.renote.uri ? data.renote.uri : `${config.url}/notes/${data.renote.id}`, note) - : renderCreate(await renderNote(note, false), note); - - return renderActivity(content); -} - function incRenoteCount(renote: Note): void { Notes.createQueryBuilder().update() .set({ From 63665e8bd1fa76c19f778c31e174fd94a727e9bd Mon Sep 17 00:00:00 2001 From: Johann150 Date: Sat, 4 Feb 2023 00:21:13 +0100 Subject: [PATCH 35/46] client: replace array concat with Array.prototype.flat --- packages/client/src/components/cw-button.vue | 5 ++--- packages/client/src/components/mfm.ts | 5 ++--- packages/client/src/scripts/array.ts | 9 +-------- 3 files changed, 5 insertions(+), 14 deletions(-) diff --git a/packages/client/src/components/cw-button.vue b/packages/client/src/components/cw-button.vue index c162f759c..c68552ab0 100644 --- a/packages/client/src/components/cw-button.vue +++ b/packages/client/src/components/cw-button.vue @@ -9,7 +9,6 @@ import { computed } from 'vue'; import { length } from 'stringz'; import * as foundkey from 'foundkey-js'; -import { concat } from '@/scripts/array'; import { i18n } from '@/i18n'; const props = defineProps<{ @@ -22,12 +21,12 @@ const emit = defineEmits<{ }>(); const label = computed(() => { - return concat([ + return [ props.note.text ? [i18n.t('_cw.chars', { count: length(props.note.text) })] : [], props.note.files && props.note.files.length !== 0 ? [i18n.t('_cw.files', { count: props.note.files.length }) ] : [], props.note.poll != null ? [i18n.ts.poll] : [], props.note.renoteId != null ? [i18n.ts.quote] : [], - ] as string[][]).join(' / '); + ].flat().join(' / '); }); const toggle = () => { diff --git a/packages/client/src/components/mfm.ts b/packages/client/src/components/mfm.ts index 466fcbdb0..ef083eb4d 100644 --- a/packages/client/src/components/mfm.ts +++ b/packages/client/src/components/mfm.ts @@ -4,7 +4,6 @@ import MkUrl from '@/components/global/url.vue'; import MkLink from '@/components/link.vue'; import MkMention from '@/components/mention.vue'; import MkEmoji from '@/components/global/emoji.vue'; -import { concat } from '@/scripts/array'; import MkFormula from '@/components/formula.vue'; import MkCode from '@/components/code.vue'; import MkSearch from '@/components/mfm-search.vue'; @@ -50,7 +49,7 @@ export default defineComponent({ return t.match(/^[0-9.]+s$/) ? t : null; }; - const genEl = (ast: mfm.MfmNode[]) => concat(ast.map((token): VNode[] => { + const genEl = (ast: mfm.MfmNode[]) => ast.map((token): VNode[] => { switch (token.type) { case 'text': { const text = token.props.text.replace(/(\r\n|\n|\r)/g, '\n'); @@ -314,7 +313,7 @@ export default defineComponent({ return []; } } - })); + }).flat(); // Parse ast to DOM return h('span', genEl(ast)); diff --git a/packages/client/src/scripts/array.ts b/packages/client/src/scripts/array.ts index 26c6195d6..e5b9016c0 100644 --- a/packages/client/src/scripts/array.ts +++ b/packages/client/src/scripts/array.ts @@ -15,19 +15,12 @@ export function count(a: T, xs: T[]): number { return countIf(x => x === a, xs); } -/** - * Concatenate an array of arrays - */ -export function concat(xss: T[][]): T[] { - return ([] as T[]).concat(...xss); -} - /** * Intersperse the element between the elements of the array * @param sep The element to be interspersed */ export function intersperse(sep: T, xs: T[]): T[] { - return concat(xs.map(x => [sep, x])).slice(1); + return xs.map(x => [sep, x]).flat().slice(1); } /** From a8c0e1f8278c4df364d539626948fd797a1f128f Mon Sep 17 00:00:00 2001 From: Mia Herkt Date: Sat, 4 Feb 2023 11:00:52 +0100 Subject: [PATCH 36/46] fix migration for note.url unique index fixes https://akkoma.dev/FoundKeyGang/FoundKey/issues/331 Co-authored-by: Johann150 --- packages/backend/migration/1674499888924-sync-orm.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/backend/migration/1674499888924-sync-orm.js b/packages/backend/migration/1674499888924-sync-orm.js index aac0d22e9..12177b559 100644 --- a/packages/backend/migration/1674499888924-sync-orm.js +++ b/packages/backend/migration/1674499888924-sync-orm.js @@ -4,13 +4,19 @@ export class syncOrm1674499888924 { async up(queryRunner) { await queryRunner.query(`COMMENT ON COLUMN "user"."token" IS 'The native access token of local users, or null.'`); await queryRunner.query(`ALTER TABLE "auth_session" DROP CONSTRAINT "UQ_8e001e5a101c6dca37df1a76d66"`); + + // remove human readable URL from notes where it is duplicated, so the index can be added + await queryRunner.query(`UPDATE "note" SET "url" = NULL WHERE "url" IN (SELECT "url" FROM "note" GROUP BY "url" HAVING COUNT("url") > 1)`); await queryRunner.query(`CREATE UNIQUE INDEX "IDX_71d35fceee0d0fa62b2fa8f3b2" ON "note" ("url") `); + await queryRunner.query(`CREATE UNIQUE INDEX "IDX_d9ecaed8c6dc43f3592c229282" ON "user_group_joining" ("userId", "userGroupId") `); } async down(queryRunner) { await queryRunner.query(`DROP INDEX "public"."IDX_d9ecaed8c6dc43f3592c229282"`); + await queryRunner.query(`DROP INDEX "public"."IDX_71d35fceee0d0fa62b2fa8f3b2"`); + await queryRunner.query(`ALTER TABLE "auth_session" ADD CONSTRAINT "UQ_8e001e5a101c6dca37df1a76d66" UNIQUE ("accessTokenId")`); await queryRunner.query(`COMMENT ON COLUMN "user"."token" IS 'The native access token of the User. It will be null if the origin of the user is local.'`); } From 9458045c8f25f749f235d27374d9810e14669c26 Mon Sep 17 00:00:00 2001 From: Johann150 Date: Sat, 4 Feb 2023 15:32:25 +0100 Subject: [PATCH 37/46] server: refactor note/renote rendering to separate file --- .../activitypub/renderer/note-or-renote.ts | 17 +++++++++++++++++ packages/backend/src/server/activitypub.ts | 5 +++-- .../backend/src/server/activitypub/outbox.ts | 16 ++-------------- packages/backend/src/services/note/create.ts | 4 ++-- 4 files changed, 24 insertions(+), 18 deletions(-) create mode 100644 packages/backend/src/remote/activitypub/renderer/note-or-renote.ts diff --git a/packages/backend/src/remote/activitypub/renderer/note-or-renote.ts b/packages/backend/src/remote/activitypub/renderer/note-or-renote.ts new file mode 100644 index 000000000..cb013140c --- /dev/null +++ b/packages/backend/src/remote/activitypub/renderer/note-or-renote.ts @@ -0,0 +1,17 @@ +import config from '@/config/index.js'; +import { Notes } from '@/models/index.js'; +import { Note } from '@/models/entities/note.js'; +import { isPureRenote } from '@/misc/renote.js'; +import { IActivity } from '@/remote/activitypub/types.js'; +import renderNote from '@/remote/activitypub/renderer/note.js'; +import renderCreate from '@/remote/activitypub/renderer/create.js'; +import renderAnnounce from '@/remote/activitypub/renderer/announce.js'; + +export async function renderNoteOrRenoteActivity(note: Note): Promise { + if (isPureRenote(note)) { + const renote = await Notes.findOneByOrFail({ id: note.renoteId }); + return renderAnnounce(renote.uri ?? `${config.url}/notes/${renote.id}`, note); + } else { + return renderCreate(await renderNote(note, false), note); + } +} diff --git a/packages/backend/src/server/activitypub.ts b/packages/backend/src/server/activitypub.ts index 2dc4ea1e7..6bec78f8c 100644 --- a/packages/backend/src/server/activitypub.ts +++ b/packages/backend/src/server/activitypub.ts @@ -15,7 +15,8 @@ import { ILocalUser, User } from '@/models/entities/user.js'; import { renderLike } from '@/remote/activitypub/renderer/like.js'; import { getUserKeypair } from '@/misc/keypair-store.js'; import renderFollow from '@/remote/activitypub/renderer/follow.js'; -import Outbox, { packActivity } from './activitypub/outbox.js'; +import { renderNoteOrRenoteActivity } from '@/remote/activitypub/renderer/note-or-renote.js'; +import Outbox from './activitypub/outbox.js'; import Followers from './activitypub/followers.js'; import Following from './activitypub/following.js'; import Featured from './activitypub/featured.js'; @@ -115,7 +116,7 @@ router.get('/notes/:note/activity', async ctx => { return; } - ctx.body = renderActivity(await packActivity(note)); + ctx.body = renderActivity(await renderNoteOrRenoteActivity(note)); ctx.set('Cache-Control', 'public, max-age=180'); setResponseType(ctx); }); diff --git a/packages/backend/src/server/activitypub/outbox.ts b/packages/backend/src/server/activitypub/outbox.ts index d517ffcfe..7b8fefb3b 100644 --- a/packages/backend/src/server/activitypub/outbox.ts +++ b/packages/backend/src/server/activitypub/outbox.ts @@ -7,6 +7,7 @@ import renderOrderedCollectionPage from '@/remote/activitypub/renderer/ordered-c import renderNote from '@/remote/activitypub/renderer/note.js'; import renderCreate from '@/remote/activitypub/renderer/create.js'; import renderAnnounce from '@/remote/activitypub/renderer/announce.js'; +import { renderNoteOrRenoteActivity } from '@/remote/activitypub/renderer/note-or-renote.js'; import { countIf } from '@/prelude/array.js'; import * as url from '@/prelude/url.js'; import { Users, Notes } from '@/models/index.js'; @@ -63,7 +64,7 @@ export default async (ctx: Router.RouterContext) => { if (sinceId) notes.reverse(); - const activities = await Promise.all(notes.map(note => packActivity(note))); + const activities = await Promise.all(notes.map(note => renderNoteOrRenoteActivity(note))); const rendered = renderOrderedCollectionPage( `${partOf}?${url.query({ page: 'true', @@ -94,16 +95,3 @@ export default async (ctx: Router.RouterContext) => { setResponseType(ctx); } }; - -/** - * Pack Create or Announce Activity - * @param note Note - */ -export async function packActivity(note: Note): Promise { - if (isPureRenote(note)) { - const renote = await Notes.findOneByOrFail({ id: note.renoteId }); - return renderAnnounce(renote.uri ?? `${config.url}/notes/${renote.id}`, note); - } else { - return renderCreate(await renderNote(note, false), note); - } -} diff --git a/packages/backend/src/services/note/create.ts b/packages/backend/src/services/note/create.ts index 04b4a22a5..8f48bc12a 100644 --- a/packages/backend/src/services/note/create.ts +++ b/packages/backend/src/services/note/create.ts @@ -36,7 +36,7 @@ import { Cache } from '@/misc/cache.js'; import { UserProfile } from '@/models/entities/user-profile.js'; import { getActiveWebhooks } from '@/misc/webhook-cache.js'; import { IActivity } from '@/remote/activitypub/type.js'; -import { packActivity } from '@/server/activitypub/outbox.js'; +import { renderNoteOrRenoteActivity } from '@/remote/activitypub/renderer/note-or-renote.js'; import { MINUTE } from '@/const.js'; import { updateHashtags } from '../update-hashtag.js'; import { registerOrFetchInstanceDoc } from '../register-or-fetch-instance-doc.js'; @@ -431,7 +431,7 @@ export default async (user: { id: User['id']; username: User['username']; host: //#region AP deliver if (Users.isLocalUser(user) && !data.localOnly) { (async () => { - const noteActivity = renderActivity(await packActivity(note)); + const noteActivity = renderActivity(await renderNoteOrRenoteActivity(note)); const dm = new DeliverManager(user, noteActivity); // Delivered to remote users who have been mentioned From 28c11ca7afb7e47c3fe55180eda3ebd8c083992b Mon Sep 17 00:00:00 2001 From: Johann150 Date: Sat, 4 Feb 2023 15:58:13 +0100 Subject: [PATCH 38/46] refactor isPureRenote to foundkey-js --- packages/backend/src/misc/renote.ts | 12 ------------ .../remote/activitypub/renderer/note-or-renote.ts | 4 ++-- packages/backend/src/server/activitypub/outbox.ts | 1 - .../backend/src/server/api/endpoints/notes/create.ts | 7 +++---- packages/backend/src/services/note/delete.ts | 4 ++-- packages/client/src/components/note-detailed.vue | 7 +------ packages/client/src/components/note.vue | 8 +------- packages/client/src/scripts/get-note-menu.ts | 11 +++-------- packages/foundkey-js/src/entities.ts | 11 +++++++++++ 9 files changed, 23 insertions(+), 42 deletions(-) delete mode 100644 packages/backend/src/misc/renote.ts diff --git a/packages/backend/src/misc/renote.ts b/packages/backend/src/misc/renote.ts deleted file mode 100644 index 5b2657633..000000000 --- a/packages/backend/src/misc/renote.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { Note } from '@/models/entities/note.js'; - -export function isPureRenote(note: Note): note is Note & { renoteId: string, text: null, cw: null, fileIds: null | never[], hasPoll: false } { - return note.renoteId != null - && note.text == null - && note.cw == null - && ( - note.fileIds == null - || note.fileIds.length === 0 - ) - && !note.hasPoll; -} diff --git a/packages/backend/src/remote/activitypub/renderer/note-or-renote.ts b/packages/backend/src/remote/activitypub/renderer/note-or-renote.ts index cb013140c..0857cd5b2 100644 --- a/packages/backend/src/remote/activitypub/renderer/note-or-renote.ts +++ b/packages/backend/src/remote/activitypub/renderer/note-or-renote.ts @@ -1,14 +1,14 @@ +import * as foundkey from 'foundkey-js'; import config from '@/config/index.js'; import { Notes } from '@/models/index.js'; import { Note } from '@/models/entities/note.js'; -import { isPureRenote } from '@/misc/renote.js'; import { IActivity } from '@/remote/activitypub/types.js'; import renderNote from '@/remote/activitypub/renderer/note.js'; import renderCreate from '@/remote/activitypub/renderer/create.js'; import renderAnnounce from '@/remote/activitypub/renderer/announce.js'; export async function renderNoteOrRenoteActivity(note: Note): Promise { - if (isPureRenote(note)) { + if (foundkey.entities.isPureRenote(note)) { const renote = await Notes.findOneByOrFail({ id: note.renoteId }); return renderAnnounce(renote.uri ?? `${config.url}/notes/${renote.id}`, note); } else { diff --git a/packages/backend/src/server/activitypub/outbox.ts b/packages/backend/src/server/activitypub/outbox.ts index 7b8fefb3b..a0a6af011 100644 --- a/packages/backend/src/server/activitypub/outbox.ts +++ b/packages/backend/src/server/activitypub/outbox.ts @@ -12,7 +12,6 @@ import { countIf } from '@/prelude/array.js'; import * as url from '@/prelude/url.js'; import { Users, Notes } from '@/models/index.js'; import { Note } from '@/models/entities/note.js'; -import { isPureRenote } from '@/misc/renote.js'; import { makePaginationQuery } from '../api/common/make-pagination-query.js'; import { setResponseType } from '../activitypub.js'; diff --git a/packages/backend/src/server/api/endpoints/notes/create.ts b/packages/backend/src/server/api/endpoints/notes/create.ts index 1b272e9dc..d10ba4df0 100644 --- a/packages/backend/src/server/api/endpoints/notes/create.ts +++ b/packages/backend/src/server/api/endpoints/notes/create.ts @@ -1,5 +1,5 @@ import { In } from 'typeorm'; -import { noteVisibilities } from 'foundkey-js'; +import { noteVisibilities, entities } from 'foundkey-js'; import create from '@/services/note/create.js'; import { User } from '@/models/entities/user.js'; import { Users, DriveFiles, Notes, Channels, Blockings } from '@/models/index.js'; @@ -7,7 +7,6 @@ import { DriveFile } from '@/models/entities/drive-file.js'; import { Note } from '@/models/entities/note.js'; import { Channel } from '@/models/entities/channel.js'; import { HOUR } from '@/const.js'; -import { isPureRenote } from '@/misc/renote.js'; import config from '@/config/index.js'; import { ApiError } from '../../error.js'; import define from '../../define.js'; @@ -160,7 +159,7 @@ export default define(meta, paramDef, async (ps, user) => { throw e; }); - if (isPureRenote(renote)) throw new ApiError('PURE_RENOTE', 'Cannot renote a pure renote.'); + if (entities.isPureRenote(renote)) throw new ApiError('PURE_RENOTE', 'Cannot renote a pure renote.'); // check that the visibility is not less restrictive if (noteVisibilities.indexOf(renote.visibility) > noteVisibilities.indexOf(ps.visibility)) { @@ -185,7 +184,7 @@ export default define(meta, paramDef, async (ps, user) => { throw e; }); - if (isPureRenote(reply)) throw new ApiError('PURE_RENOTE', 'Cannot reply to a pure renote.'); + if (entities.isPureRenote(reply)) throw new ApiError('PURE_RENOTE', 'Cannot reply to a pure renote.'); // check that the visibility is not less restrictive if (noteVisibilities.indexOf(reply.visibility) > noteVisibilities.indexOf(ps.visibility)) { diff --git a/packages/backend/src/services/note/delete.ts b/packages/backend/src/services/note/delete.ts index 6075f15b1..1df08d5ea 100644 --- a/packages/backend/src/services/note/delete.ts +++ b/packages/backend/src/services/note/delete.ts @@ -1,4 +1,5 @@ import { FindOptionsWhere, In, IsNull, Not } from 'typeorm'; +import * as foundkey from 'foundkey-js'; import { publishNoteStream } from '@/services/stream.js'; import renderDelete from '@/remote/activitypub/renderer/delete.js'; import renderAnnounce from '@/remote/activitypub/renderer/announce.js'; @@ -12,7 +13,6 @@ import { Notes, Users, Instances } from '@/models/index.js'; import { notesChart, perUserNotesChart, instanceChart } from '@/services/chart/index.js'; import { DeliverManager } from '@/remote/activitypub/deliver-manager.js'; import { countSameRenotes } from '@/misc/count-same-renotes.js'; -import { isPureRenote } from '@/misc/renote.js'; import { registerOrFetchInstanceDoc } from '../register-or-fetch-instance-doc.js'; import { deliverToRelays } from '../relay.js'; @@ -42,7 +42,7 @@ export default async function(user: { id: User['id']; uri: User['uri']; host: Us let renote: Note | null = null; // if deleted note is renote - if (isPureRenote(note)) { + if (foundkey.entities.isPureRenote(note)) { renote = await Notes.findOneBy({ id: note.renoteId }); } diff --git a/packages/client/src/components/note-detailed.vue b/packages/client/src/components/note-detailed.vue index e64e13375..3549ef953 100644 --- a/packages/client/src/components/note-detailed.vue +++ b/packages/client/src/components/note-detailed.vue @@ -159,12 +159,7 @@ if (noteViewInterruptors.length > 0) { }); } -const isRenote = ( - note.renote != null && - note.text == null && - note.fileIds.length === 0 && - note.poll == null -); +const isRenote = foundkey.entities.isPureRenote(note); const el = ref(); const menuButton = ref(); diff --git a/packages/client/src/components/note.vue b/packages/client/src/components/note.vue index e8758329a..b0938b3c0 100644 --- a/packages/client/src/components/note.vue +++ b/packages/client/src/components/note.vue @@ -148,13 +148,7 @@ if (noteViewInterruptors.length > 0) { }); } -const isRenote = ( - note.renote != null && - note.text == null && - note.cw == null && - note.fileIds.length === 0 && - note.poll == null -); +const isRenote = foundkey.entities.isPureRenote(note); const el = ref(); const menuButton = ref(); diff --git a/packages/client/src/scripts/get-note-menu.ts b/packages/client/src/scripts/get-note-menu.ts index 57445a49a..1afab0071 100644 --- a/packages/client/src/scripts/get-note-menu.ts +++ b/packages/client/src/scripts/get-note-menu.ts @@ -16,14 +16,9 @@ export function getNoteMenu(props: { isDeleted: Ref; currentClipPage?: Ref; }) { - const isRenote = ( - props.note.renote != null && - props.note.text == null && - props.note.fileIds.length === 0 && - props.note.poll == null - ); - - const appearNote = isRenote ? props.note.renote as foundkey.entities.Note : props.note; + const appearNote = foundkey.entities.isPureRenote(props.note) + ? props.note.renote as foundkey.entities.Note + : props.note; function del(): void { os.confirm({ diff --git a/packages/foundkey-js/src/entities.ts b/packages/foundkey-js/src/entities.ts index 3d2404b47..30dd90582 100644 --- a/packages/foundkey-js/src/entities.ts +++ b/packages/foundkey-js/src/entities.ts @@ -471,3 +471,14 @@ export type UserSorting = | '+updatedAt' | '-updatedAt'; export type OriginType = 'combined' | 'local' | 'remote'; + +export function isPureRenote(note: Note): boolean { + return note.renoteId != null + && note.text == null + && note.cw == null + && ( + note.fileIds == null + || note.fileIds.length === 0 + ) + && note.poll == null; +} From 1adf88b09013d79113418f681f15d1edb5b46a16 Mon Sep 17 00:00:00 2001 From: Johann150 Date: Sat, 4 Feb 2023 16:44:30 +0100 Subject: [PATCH 39/46] fixup: OpenGraph data generation This is a fixup for commits 39fb7e594624957ec2e0f15ec2ec6332901587af and be30e70344cb2862b4397ce0a5927dfda0014c2d. --- packages/backend/src/server/web/index.ts | 2 +- packages/backend/src/server/web/views/note.pug | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/backend/src/server/web/index.ts b/packages/backend/src/server/web/index.ts index 4d1726421..fec6663fa 100644 --- a/packages/backend/src/server/web/index.ts +++ b/packages/backend/src/server/web/index.ts @@ -331,7 +331,7 @@ router.get('/notes/:note', async (ctx, next) => { // If the note has a CW (is sensitive as a whole) or any of the files is sensitive or there are no // files, they are not used for a preview. let filesOpengraph = []; - if (!packedNote.cw || packedNote.files.length > 0 || packedNote.files.all(file => !file.isSensitive)) { + if (!packedNote.cw || packedNote.files.length > 0 || packedNote.files.every(file => !file.isSensitive)) { let limit = 4; for (const file of packedNote.files) { if (file.type.startsWith('image/')) { diff --git a/packages/backend/src/server/web/views/note.pug b/packages/backend/src/server/web/views/note.pug index ae5cc0363..02b61f572 100644 --- a/packages/backend/src/server/web/views/note.pug +++ b/packages/backend/src/server/web/views/note.pug @@ -14,7 +14,7 @@ block desc block og meta(property='og:type' content='article') - meta(property='og:article:published_time' content=note.createdAt.toISOString()) + meta(property='og:article:published_time' content=note.createdAt) meta(property='og:article:author:username' content=user.username) meta(property='og:title' content= title) meta(property='og:description' content= summary) From 9a6bb8be7de3acee4815339cdeb12d1a925505a5 Mon Sep 17 00:00:00 2001 From: Johann150 Date: Sat, 4 Feb 2023 17:56:15 +0100 Subject: [PATCH 40/46] server: default config items on load --- .config/example.yml | 50 +++++++++++++++-------- packages/backend/src/config/load.ts | 36 ++++++++-------- packages/backend/src/misc/download-url.ts | 9 ++-- packages/backend/src/misc/fetch.ts | 2 +- packages/backend/src/queue/index.ts | 8 ++-- packages/backend/src/queue/queues.ts | 4 +- 6 files changed, 65 insertions(+), 44 deletions(-) diff --git a/.config/example.yml b/.config/example.yml index 2924114fb..0cf399a64 100644 --- a/.config/example.yml +++ b/.config/example.yml @@ -6,10 +6,11 @@ #───┘ URL └───────────────────────────────────────────────────── # Final accessible URL seen by a user. -url: https://example.tld/ - +# Only the host part will be used. # ONCE YOU HAVE STARTED THE INSTANCE, DO NOT CHANGE THE # URL SETTINGS AFTER THAT! +url: https://example.tld/ + # ┌───────────────────────┐ #───┘ Port and TLS settings └─────────────────────────────────── @@ -45,6 +46,7 @@ db: pass: example-foundkey-pass # Whether to disable query caching + # Default is to cache, i.e. false. #disableCache: true # Extra connection options @@ -57,7 +59,11 @@ db: redis: host: localhost port: 6379 - #family: dual # can be either a number or string (0/dual, 4/ipv4, 6/ipv6) + # Address family to connect over. + # Can be either a number or string (0/dual, 4/ipv4, 6/ipv6) + # Default is "dual". + #family: dual + # The following properties are optional. #pass: example-pass #prefix: example-prefix #db: 1 @@ -65,6 +71,7 @@ redis: # ┌─────────────────────────────┐ #───┘ Elasticsearch configuration └───────────────────────────── +# Elasticsearch is optional. #elasticsearch: # host: localhost # port: 9200 @@ -75,35 +82,41 @@ redis: # ┌─────────────────────┐ #───┘ Other configuration └───────────────────────────────────── -# Whether disable HSTS +# Whether to disable HSTS (not recommended) +# Default is to enable HSTS, i.e. false. #disableHsts: true # Number of worker processes by type. -# The sum must not exceed the number of available cores. +# The sum should not exceed the number of available cores. #clusterLimits: # web: 1 # queue: 1 -# Job concurrency per worker -# deliverJobConcurrency: 128 -# inboxJobConcurrency: 16 +# Jobs each worker will try to work on at a time. +#deliverJobConcurrency: 128 +#inboxJobConcurrency: 16 -# Job rate limiter -# deliverJobPerSec: 128 -# inboxJobPerSec: 16 +# Rate limit for each Worker. +# Use -1 to disable. +# A rate limit for deliver jobs is not recommended as it comes with +# a big performance penalty due to overhead of rate limiting. +#deliverJobPerSec: 128 +#inboxJobPerSec: 16 -# Job attempts -# deliverJobMaxAttempts: 12 -# inboxJobMaxAttempts: 8 +# Number of times each job will be tried. +# 1 means only try once and don't retry. +#deliverJobMaxAttempts: 12 +#inboxJobMaxAttempts: 8 # Syslog option #syslog: # host: localhost # port: 514 -# Proxy for HTTP/HTTPS +# Proxy for HTTP/HTTPS outgoing connections #proxy: http://127.0.0.1:3128 +# Hosts that should not be connected to through the proxy specified above #proxyBypassHosts: [ # 'example.com', # '192.0.2.8' @@ -117,7 +130,8 @@ redis: # Media Proxy #mediaProxy: https://example.com/proxy -# Proxy remote files (default: false) +# Proxy remote files +# Default is to not proxy remote files, i.e. false. #proxyRemoteFiles: true # Storage path for files if stored locally (absolute path) @@ -125,11 +139,15 @@ redis: #internalStoragePath: '/etc/foundkey/files' # Upload or download file size limits (bytes) +# default is 262144000 = 250MiB #maxFileSize: 262144000 # Max note text length (in characters) #maxNoteTextLength: 3000 +# By default, Foundkey will fail when something tries to make it fetch something from private IPs. +# With the following setting you can explicitly allow some private CIDR subnets. +# Default is an empty list, i.e. none allowed. #allowedPrivateNetworks: [ # '127.0.0.1/32' #] diff --git a/packages/backend/src/config/load.ts b/packages/backend/src/config/load.ts index 95286585d..9adf5677b 100644 --- a/packages/backend/src/config/load.ts +++ b/packages/backend/src/config/load.ts @@ -38,13 +38,30 @@ export default function load(): Config { config.port = config.port || parseInt(process.env.PORT || '', 10); + // set default values config.images = Object.assign({ info: '/twemoji/1f440.svg', notFound: '/twemoji/2049.svg', error: '/twemoji/1f480.svg', }, config.images ?? {}); - if (!config.maxNoteTextLength) config.maxNoteTextLength = 3000; + config.clusterLimits = Object.assign({ + web: 1, + queue: 1, + }, config.clusterLimits ?? {}); + + config = Object.assign({ + disableHsts: false, + deliverJobConcurrency: 128, + inboxJobConcurrency: 16, + deliverJobPerSec: 128, + inboxJobPerSec: 16, + deliverJobMaxAttempts: 12, + inboxJobMaxAttempts: 8, + proxyRemoteFiles: false, + maxFileSize: 262144000, // 250 MiB + maxNoteTextLength: 3000, + }, config); mixin.version = meta.version; mixin.host = url.host; @@ -60,21 +77,8 @@ export default function load(): Config { if (!config.redis.prefix) config.redis.prefix = mixin.host; - if (!config.clusterLimits) { - config.clusterLimits = { - web: 1, - queue: 1, - }; - } else { - config.clusterLimits = { - web: 1, - queue: 1, - ...config.clusterLimits, - }; - - if (config.clusterLimits.web < 1 || config.clusterLimits.queue < 1) { - throw new Error('invalid cluster limits'); - } + if (config.clusterLimits.web < 1 || config.clusterLimits.queue < 1) { + throw new Error('invalid cluster limits'); } return Object.assign(config, mixin); diff --git a/packages/backend/src/misc/download-url.ts b/packages/backend/src/misc/download-url.ts index 079e84e8a..694a80457 100644 --- a/packages/backend/src/misc/download-url.ts +++ b/packages/backend/src/misc/download-url.ts @@ -19,7 +19,6 @@ export async function downloadUrl(url: string, path: string): Promise { const timeout = 30 * SECOND; const operationTimeout = MINUTE; - const maxSize = config.maxFileSize || 262144000; const req = got.stream(url, { headers: { @@ -53,14 +52,14 @@ export async function downloadUrl(url: string, path: string): Promise { const contentLength = res.headers['content-length']; if (contentLength != null) { const size = Number(contentLength); - if (size > maxSize) { - logger.warn(`maxSize exceeded (${size} > ${maxSize}) on response`); + if (size > config.maxFileSize) { + logger.warn(`maxSize exceeded (${size} > ${config.maxFileSize}) on response`); req.destroy(); } } }).on('downloadProgress', (progress: Got.Progress) => { - if (progress.transferred > maxSize) { - logger.warn(`maxSize exceeded (${progress.transferred} > ${maxSize}) on downloadProgress`); + if (progress.transferred > config.maxFileSize) { + logger.warn(`maxSize exceeded (${progress.transferred} > ${config.maxFileSize}) on downloadProgress`); req.destroy(); } }); diff --git a/packages/backend/src/misc/fetch.ts b/packages/backend/src/misc/fetch.ts index 42eb445d9..6bd794e68 100644 --- a/packages/backend/src/misc/fetch.ts +++ b/packages/backend/src/misc/fetch.ts @@ -89,7 +89,7 @@ const _https = new https.Agent({ lookup: cache.lookup, } as https.AgentOptions); -const maxSockets = Math.max(256, config.deliverJobConcurrency || 128); +const maxSockets = Math.max(256, config.deliverJobConcurrency); /** * Get http proxy or non-proxy agent diff --git a/packages/backend/src/queue/index.ts b/packages/backend/src/queue/index.ts index 57de62a79..cc125a3de 100644 --- a/packages/backend/src/queue/index.ts +++ b/packages/backend/src/queue/index.ts @@ -96,7 +96,7 @@ export function deliver(user: ThinUser, content: unknown, to: string | null) { }; return deliverQueue.add(data, { - attempts: config.deliverJobMaxAttempts || 12, + attempts: config.deliverJobMaxAttempts, timeout: MINUTE, backoff: { type: 'apBackoff', @@ -113,7 +113,7 @@ export function inbox(activity: IActivity, signature: httpSignature.IParsedSigna }; return inboxQueue.add(data, { - attempts: config.inboxJobMaxAttempts || 8, + attempts: config.inboxJobMaxAttempts, timeout: 5 * MINUTE, backoff: { type: 'apBackoff', @@ -291,8 +291,8 @@ export function webhookDeliver(webhook: Webhook, type: typeof webhookEventTypes[ export default function() { if (envOption.onlyServer) return; - deliverQueue.process(config.deliverJobConcurrency || 128, processDeliver); - inboxQueue.process(config.inboxJobConcurrency || 16, processInbox); + deliverQueue.process(config.deliverJobConcurrency, processDeliver); + inboxQueue.process(config.inboxJobConcurrency, processInbox); endedPollNotificationQueue.process(endedPollNotification); webhookDeliverQueue.process(64, processWebhookDeliver); processDb(dbQueue); diff --git a/packages/backend/src/queue/queues.ts b/packages/backend/src/queue/queues.ts index f3a267790..0f0f2f69d 100644 --- a/packages/backend/src/queue/queues.ts +++ b/packages/backend/src/queue/queues.ts @@ -4,8 +4,8 @@ import { DeliverJobData, InboxJobData, DbJobData, ObjectStorageJobData, EndedPol export const systemQueue = initializeQueue>('system'); export const endedPollNotificationQueue = initializeQueue('endedPollNotification'); -export const deliverQueue = initializeQueue('deliver', config.deliverJobPerSec || 128); -export const inboxQueue = initializeQueue('inbox', config.inboxJobPerSec || 16); +export const deliverQueue = initializeQueue('deliver', config.deliverJobPerSec); +export const inboxQueue = initializeQueue('inbox', config.inboxJobPerSec); export const dbQueue = initializeQueue('db'); export const objectStorageQueue = initializeQueue('objectStorage'); export const webhookDeliverQueue = initializeQueue('webhookDeliver', 64); From 41c42f96f0464ead2ef1bfb862be6085dd2fa340 Mon Sep 17 00:00:00 2001 From: Johann150 Date: Sat, 4 Feb 2023 17:57:52 +0100 Subject: [PATCH 41/46] BREAKING server: disable deliver rate limit by default The deliver rate limit seems to cause a lot of performance problems, presumably because of the overhead the rate limit has. It also does not really make sense to rate limit outgoing because we are requesting from different servers anyway. fixes https://akkoma.dev/FoundKeyGang/FoundKey/issues/190 Changelog: Changed --- .config/example.yml | 2 +- packages/backend/src/config/load.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.config/example.yml b/.config/example.yml index 0cf399a64..4146881b1 100644 --- a/.config/example.yml +++ b/.config/example.yml @@ -100,7 +100,7 @@ redis: # Use -1 to disable. # A rate limit for deliver jobs is not recommended as it comes with # a big performance penalty due to overhead of rate limiting. -#deliverJobPerSec: 128 +#deliverJobPerSec: -1 #inboxJobPerSec: 16 # Number of times each job will be tried. diff --git a/packages/backend/src/config/load.ts b/packages/backend/src/config/load.ts index 9adf5677b..133a6f4ff 100644 --- a/packages/backend/src/config/load.ts +++ b/packages/backend/src/config/load.ts @@ -54,7 +54,7 @@ export default function load(): Config { disableHsts: false, deliverJobConcurrency: 128, inboxJobConcurrency: 16, - deliverJobPerSec: 128, + deliverJobPerSec: -1, inboxJobPerSec: 16, deliverJobMaxAttempts: 12, inboxJobMaxAttempts: 8, From 839daea8877114a2d1b4116553dd8fefb0360986 Mon Sep 17 00:00:00 2001 From: Johann150 Date: Sat, 4 Feb 2023 18:08:19 +0100 Subject: [PATCH 42/46] remove mi-white.png asset --- packages/backend/assets/mi-white.png | Bin 18767 -> 0 bytes packages/backend/src/services/send-email.ts | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) delete mode 100644 packages/backend/assets/mi-white.png diff --git a/packages/backend/assets/mi-white.png b/packages/backend/assets/mi-white.png deleted file mode 100644 index 1e57da6b38197f95f848fcdcb2308e4e490cae8a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18767 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 diff --git a/packages/backend/src/services/send-email.ts b/packages/backend/src/services/send-email.ts index b4dd0d828..2a2df2d9b 100644 --- a/packages/backend/src/services/send-email.ts +++ b/packages/backend/src/services/send-email.ts @@ -8,7 +8,7 @@ export const logger = new Logger('email'); export async function sendEmail(to: string, subject: string, html: string, text: string): Promise { const meta = await fetchMeta(true); - const iconUrl = `${config.url}/static-assets/mi-white.png`; + const iconUrl = `${config.url}/static-assets/icons/512.png`; const emailSettingUrl = `${config.url}/settings/email`; const enableAuth = meta.smtpUser != null && meta.smtpUser !== ''; From d655bda30c8336fbfe752d34858c833b860c9f66 Mon Sep 17 00:00:00 2001 From: Johann150 Date: Sat, 4 Feb 2023 22:15:28 +0100 Subject: [PATCH 43/46] add foundkey floofer --- COPYING | 4 ++++ README.md | 2 ++ logo.svg | Bin 0 -> 3407 bytes packages/backend/assets/api-doc.png | Bin 5542 -> 12510 bytes packages/backend/assets/apple-touch-icon.png | Bin 9828 -> 12510 bytes packages/backend/assets/favicon.ico | Bin 90022 -> 67646 bytes packages/backend/assets/favicon.png | Bin 9348 -> 12510 bytes packages/backend/assets/icons/192.png | Bin 7629 -> 22369 bytes packages/backend/assets/icons/512.png | Bin 20527 -> 108166 bytes packages/backend/assets/splash.png | Bin 23205 -> 12510 bytes 10 files changed, 6 insertions(+) create mode 100644 logo.svg diff --git a/COPYING b/COPYING index 658556e58..bfa3fc62c 100644 --- a/COPYING +++ b/COPYING @@ -21,3 +21,7 @@ https://github.com/deskjet/chiptune2.js#license libopenmpt (as part of openmpt) by OpenMPT License: BSD 3-Clause https://github.com/OpenMPT/openmpt/blob/master/LICENSE + +The logo file (logo.svg) was created by Blinry +License: [CC-BY-4.0](https://creativecommons.org/licenses/by/4.0/) +https://blinry.org/ diff --git a/README.md b/README.md index 7c3164472..2eb3c9db8 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ +![Foundkey logo](./logo.svg) + # FoundKey FoundKey is a free and open source microblogging server compatible with ActivityPub. Forked from Misskey, FoundKey improves on maintainability and behaviour, while also bringing in useful features. diff --git a/logo.svg b/logo.svg new file mode 100644 index 0000000000000000000000000000000000000000..fec1e0be96b3270d3c64e2470c65b4f7bf39c3ef GIT binary patch literal 3407 zcmcJS%aR&L5Ji8bqqUJ1)YP-98VN1D@k-%^Lm$v$V8CKv0?>@4ulKpdbYs1|aX3T{ zO+E5)^JZr2&FS&3dD`BdH;dL;YujeC+TLx>gl(`=S6E9+xRY;RKob(HlOcz z^V7qyTrJv%^TZZz>yH0m+zV+t^|7v{m&2hC}v@eFX`um}6 z?wdFP6 zhvB>#k(eTWz25G2^H0a!%NHMB)gQC#-syZid|b_H^5@RP>{24?x~TA$!^8Y|__V+M z=i*<7?f$abHss)4UrgUO-WOBCfP=X-m%rkRn>kZ#o*5szrLn;ompbEIGQoAmo0y!j znFZqt>wPl5L}c)d=Q{sp?4-t-P>45G88>3j$zXbY#x?ICXzx?a!E6#XtLPScz32$6idCkelUgG$VGi)qK`P#r6o`J>P(Lg@2i8k5MXbj z^&H`T_WjHdUn#`jySxM70unY1rt_U-ROin;icI&q4m%;?W0h%Lc1VLlO4DM(S#ZMoRe_aZ9Ev!ETn)2qiwtK> zha3l(nk3VaPQE!}OfocGb)2__=z?TC?LuPNfe`_W_PHOZjDQ(H53v&a~5sQ0t!bCitBaA;;?DA?+#IXDWZ)dzhHmEC|A zzqZZ!IP6dB!|{I6*2mp&UcH>9&?U5W&CHQNOHBQ%=CmAktCtCU03^Rs4gX8=zF4o< z|4Zf4?M~2*t|bUP)Xe$|4RjiP<(^yj9 zOD^e?J-nht6qH=Kjq!-df;Y&R>OfV76lqkMwnY0#R3+&RK%fXwJ|3++3#vdq(_*2% z0v9ReZt58}f?_Cn2Wo&v;!(2U?4z6yd(DnAHAd3D!C#0`zdQo}kz8 zdw}kICLIB6IJ$$Ps$aZj+)Az|fUxSOuc!Jm zs2=Iw6bb1F%B2!PBaCzbHQ}k0AU`I8A{2d|L#&HDdq}3puXqI2CRtD>u1bXAB|_23 z71SvrSn})@Hgp1bsd~v2x|8d~c(tO7x?Qj#wkP_9COQP}QgJy{(XV zw9DpXQk=I8q*_w4ChseFS{HO4r|1n8dLYm0!p0li1LDtb^$S1#hT=Mcgd(WIIHn& z3;u_oo&X<_8mxmtb-uXt^&OOpA@Wv0!V8H=j9BD?3TW0jSD4h4RCoqU!%`M9gIcpK zKGzk;8RLxpjw(PVXqoG@WvmhbvDLC7+(>+y=)9QF!}#T})*%i6wF{R~DX3#G;!wuJ zj9FwSa#~JIcu^pS!-F>D!O6=hvbK1LM`bY-;OF9nYG`YC*kpj!3H23 yqtr{>=s5oV;J8(Ce&)GoEYBekEti)*U!bPPv=HFUjRy^QUMT+a4A*~G-u?|yJp7jc literal 0 HcmV?d00001 diff --git a/packages/backend/assets/api-doc.png b/packages/backend/assets/api-doc.png index 9b07f1f39805081b6a407095d8c13fbf2805825d..bc53c252d11cd4c2df20dac380f3ef02666181ec 100644 GIT binary patch literal 12510 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 Y0Dn85KUKh91VI2=$4L9BmSfaE0N>yk1ONa4 diff --git a/packages/backend/assets/apple-touch-icon.png b/packages/backend/assets/apple-touch-icon.png index 947c513bbbe6ebbdfc81b195e91d0357dbf5925a..bc53c252d11cd4c2df20dac380f3ef02666181ec 100644 GIT binary patch literal 12510 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 diff --git a/packages/backend/assets/favicon.ico b/packages/backend/assets/favicon.ico index 9be1ff62956cd5d1e21540bee043a3c6c144d453..2ca6f87baf3e96f8458cead4a1884417e41e5b75 100644 GIT binary patch literal 67646 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 z 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

{1aOs2n8j4YwUt%^~%6`1rTT0MrLHX+0OLql0X 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

~5%;b~B=NO!w0Nlsa{ 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 Hu0mjf)k>?W literal 7629 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 T+_(MnU)bAfx~hfB(BS_E(@kjH diff --git a/packages/backend/assets/icons/512.png b/packages/backend/assets/icons/512.png index ba51546427d4bdd2c7411b34d2898513c21ffa09..24f65815c7a73c936b389bd28810569116f598e3 100644 GIT binary patch literal 108166 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%0DmQb-6<&{1P;V% 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

y0WYB#YrIIhJcM3M{YJr#xLeelAn= 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 az{wDqEm~z(E93*WP3fhYT&1jO=>Gs+^GPWH literal 20527 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 diff --git a/packages/backend/assets/splash.png b/packages/backend/assets/splash.png index 3430e6efe7c29a6a0e3255de8304c4ac1bcfdd97..bc53c252d11cd4c2df20dac380f3ef02666181ec 100644 GIT binary patch literal 12510 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 Date: Sat, 4 Feb 2023 22:22:00 +0100 Subject: [PATCH 44/46] update documents for new release --- CHANGELOG.md | 102 ++++++++++++++++++++++++++++++ package.json | 2 +- packages/backend/package.json | 2 +- packages/client/package.json | 2 +- packages/foundkey-js/package.json | 2 +- packages/sw/package.json | 2 +- 6 files changed, 107 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4c5297e68..24c4bbb41 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,108 @@ Unreleased changes should not be listed in this file. Instead, run `git shortlog --format='%h %s' --group=trailer:changelog ..` to see unreleased changes; replace `` with the tag you wish to compare from. If you are a contributor, please read [CONTRIBUTING.md, section "Changelog Trailer"](./CONTRIBUTING.md#changelog-trailer) on what to do instead. +## 13.0.0-preview4 - 2023-02-05 +This release contains 6 breaking changes, including changes to the configuration file format. + +### Added +- new Foundkey logo +- client: add button to unrenote/remove all own renotes +- client: add mod tracker +- client: add button to delete all files of a user for moderators +- server: implement OAuth 2.0 Authorization Code grant +- server: add config for error images +- server: expire notifications after 3 months +- server: start adding /api/v2 routes +- server: indicate Retry-After when rate limiting +- docs: show rate limit information + +### Changed +- **BREAKING** server: implement separate web workers + The configuration file format has been changed: The `clusterLimit` item has been removed + and `clusterLimits` has been added instead. Check the example configuration file. +- **BREAKING** server: remove wildcard blocking and instead block subdomains (#269) + As an administrator you may need to check the list of blocked instances. +- **BREAKING** server: disable deliver rate limit by default + We found that the deliver rate limit causes a lot of load for no real benefit. Because of this, + it will be disabled by default. The default value of `deliverJobPerSec` is set to + disable this rate limit. +- server: adjust permissions for `/api/admin/accounts/delete` + The admin/accounts/delete endpoint now requries administrator privileges + instead of just moderator privileges. +- server: increase nodeinfo caching +- client: headlines in queue widget are links +- client: add tooltips to visibility icons +- server: improve error messages +- server: change default value for `/api/admin/show-users` origin param +- server: lower rate limit for deletion activities + Deleting things that result in federating a delete activity have a more strict rate limit. + This affects the following endpoints: + - `/api/notes/delete` + - `/api/notes/reactions/delete` + - `/api/notes/unrenote` +- server: improve OpenGraph data + - properly render note attachments as RDFa + - add more metadata about e.g. author + - proper OpenGraph data replaces custom `misskey:` RDFa tags +- activitypub: implement [FEP-e232](https://codeberg.org/fediverse/fep/src/branch/main/feps/fep-e232.md) qoutes +- activitypub: use `quoteUri` instead of `quoteUrl` + +### Fixed +- client: fix layout of app authorization page +- client: unify different error dialogs +- client: set display name limit same as server +- client: dont display instance banner tooltip if software name is unknown +- client: fix 500 error in notifications +- client: fix some tooltips not closing +- client: fix issue of search only working once +- client: check `quoteId` for canPost computation +- client: fix quotes with only a CW +- server: fix thread mutes not applying to renotes +- server: fix ReferenceError: meta is undefined +- server: fix TypeError in registerOrFetchInstanceDoc +- server: fix ratelimit in `/api/i/import-following` +- server: handle redirects in signed get +- server: remove reversi database tables +- server: set file permissions after copy +- server: also use human readable URL in search +- server: fix user deletion race condition +- server: add websocket ping mechanism + This should help keep websocket connections alive even if there are no events for + prolonged time periods. This should also fix issues where the "connection has been lost" + dialog appeared despite the connection being fine. +- activitypub: properly parse incoming hashtags +- activitypub: Do block checks more globally +- activitypub: properly render CW only quotes + +### Removed: +- **BREAKING** server: remove Twitter, Github and Discord integrations +ff31b8b06 server: remove bios and cli +a673647fb server: remove avatarColor and bannerColor properties +- **BREAKING** server: remove `api/admin/delete-account`, + You should use the API endpoint `admin/accounts/delete` instead. + It has the same parameter and the same behaviour. +- **BREAKING** remove galleries + Galleries have been removed because low usage and duplication of other behaviour. + Existing gallery posts will be turned into ordinary notes. + If a user had any gallery posts, a new clip called "Gallery" will be created containing + all of the former gallery posts that are now notes. + This affects the following endpoints: + - `/api/gallery/featured` + - `/api/gallery/popular` + - `/api/gallery/posts` + - `/api/gallery/posts/create` + - `/api/gallery/posts/delete` + - `/api/gallery/posts/like` + - `/api/gallery/posts/show` + - `/api/gallery/posts/unlike` + - `/api/i/gallery/likes` + - `/api/i/gallery/posts` + - `/api/users/gallery/posts` +- server: remove application level websocket ping + This pinging mechanism was unused in `foundkey-js`, and we expect other usage to be low. + You can use the pinging mechanism built into the websocket protocol if you wish. + Note that the Server will now also send pings on its own (see *Fixed* section). + ## 13.0.0-preview3 - 2022-12-02 This release contains 1 urgent security fix necessitated by `misskey-forkbomb`. This release contains 1 breaking change. diff --git a/package.json b/package.json index 659ba2897..6c01e1f6f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "foundkey", - "version": "13.0.0-preview3", + "version": "13.0.0-preview4", "repository": { "type": "git", "url": "https://akkoma.dev/FoundKeyGang/FoundKey.git" diff --git a/packages/backend/package.json b/packages/backend/package.json index 5d4eca072..197ddd605 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -1,6 +1,6 @@ { "name": "backend", - "version": "13.0.0-preview3", + "version": "13.0.0-preview4", "main": "./index.js", "private": true, "type": "module", diff --git a/packages/client/package.json b/packages/client/package.json index 44aa9a9c9..d57733db2 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -1,6 +1,6 @@ { "name": "client", - "version": "13.0.0-preview3", + "version": "13.0.0-preview4", "private": true, "scripts": { "watch": "vite build --watch --mode development", diff --git a/packages/foundkey-js/package.json b/packages/foundkey-js/package.json index feaa68965..de0862919 100644 --- a/packages/foundkey-js/package.json +++ b/packages/foundkey-js/package.json @@ -1,6 +1,6 @@ { "name": "foundkey-js", - "version": "13.0.0-preview3", + "version": "13.0.0-preview4", "description": "Fork of misskey-js for Foundkey", "type": "module", "main": "./built/index.js", diff --git a/packages/sw/package.json b/packages/sw/package.json index 2c3c26ffe..41bf30605 100644 --- a/packages/sw/package.json +++ b/packages/sw/package.json @@ -1,6 +1,6 @@ { "name": "sw", - "version": "13.0.0-preview3", + "version": "13.0.0-preview4", "private": true, "scripts": { "watch": "node build.js watch", From 06ef7522187ab846c1cc2b547305ba645d60967b Mon Sep 17 00:00:00 2001 From: Johann150 Date: Sat, 4 Feb 2023 23:00:34 +0100 Subject: [PATCH 45/46] adjust readme --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 2eb3c9db8..3832b2071 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -![Foundkey logo](./logo.svg) +

Foundkey logo, an owl holding a key
# FoundKey FoundKey is a free and open source microblogging server compatible with ActivityPub. Forked from Misskey, FoundKey improves on maintainability and behaviour, while also bringing in useful features. @@ -12,4 +12,5 @@ FoundKey's documentation is a work in progress. In the meantime, much of the doc If you're interested in helping out with the project, please read the [contributing guide](./CONTRIBUTING.md). ## Sponsors -FoundKey is not interested in sponsorships. +FoundKey is not interested in finanical sponsorships. +We welcome contributions in the forms of code, testing and bug reporting (see also section *Contributing* above). From 3489c8ac3aa0a5f9a12b7a31d74e91ea5d04159d Mon Sep 17 00:00:00 2001 From: Johann150 Date: Sat, 4 Feb 2023 23:24:05 +0100 Subject: [PATCH 46/46] fix: loading config --- packages/backend/src/config/load.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/backend/src/config/load.ts b/packages/backend/src/config/load.ts index 133a6f4ff..a6431162d 100644 --- a/packages/backend/src/config/load.ts +++ b/packages/backend/src/config/load.ts @@ -26,7 +26,7 @@ const path = process.env.NODE_ENV === 'test' export default function load(): Config { const meta = JSON.parse(fs.readFileSync(`${_dirname}/../../../../built/meta.json`, 'utf-8')); const clientManifest = JSON.parse(fs.readFileSync(`${_dirname}/../../../../built/_client_dist_/manifest.json`, 'utf-8')); - const config = yaml.load(fs.readFileSync(path, 'utf-8')) as Source; + let config = yaml.load(fs.readFileSync(path, 'utf-8')) as Source; if (config.id && config.id !== 'aid') throw new Error('Unsupported ID algorithm. Only "aid" is supported.');