From 8c377b6a1aa992e35e4a4a3cbe3e9c26b70866d0 Mon Sep 17 00:00:00 2001 From: Johann150 Date: Sun, 7 May 2023 14:23:11 +0200 Subject: [PATCH 01/36] foundkey-js: remove ads, detailed instance metadata The distinction for "DetailedInstanceMetadata" does no longer exist since commit 9022ab9f2a5872c02f6d20f1d96ebc1bee6dd43f. The `DetailedInstanceMetadata` and `LiteInstanceMetadata` have therefore been removed, leaving only `InstanceMetadata`. Changelog: Removed --- packages/foundkey-js/src/api.types.ts | 19 ++----------------- packages/foundkey-js/src/entities.ts | 9 +-------- 2 files changed, 3 insertions(+), 25 deletions(-) diff --git a/packages/foundkey-js/src/api.types.ts b/packages/foundkey-js/src/api.types.ts index 8670f5cd6..4c6b098f5 100644 --- a/packages/foundkey-js/src/api.types.ts +++ b/packages/foundkey-js/src/api.types.ts @@ -1,6 +1,5 @@ import { - Ad, Announcement, Antenna, App, AuthSession, Blocking, Channel, Clip, DateString, DetailedInstanceMetadata, DriveFile, DriveFolder, Following, FollowingFolloweePopulated, FollowingFollowerPopulated, FollowRequest, Instance, InstanceMetadata, - LiteInstanceMetadata, + Announcement, Antenna, App, AuthSession, Blocking, Channel, Clip, DateString, InstanceMetadata, DriveFile, DriveFolder, Following, FollowingFolloweePopulated, FollowingFollowerPopulated, FollowRequest, Instance, MeDetailed, Note, NoteFavorite, OriginType, Page, ServerInfo, Stats, User, UserDetailed, UserGroup, UserList, UserSorting, Notification, NoteReaction, Signin, MessagingMessage, } from './entities.js'; @@ -380,21 +379,7 @@ export type Endpoints = { 'messaging/messages/create': { req: { userId?: User['id']; groupId?: UserGroup['id']; text?: string; fileId?: DriveFile['id']; }; res: MessagingMessage; }; 'messaging/messages/delete': { req: { messageId: MessagingMessage['id']; }; res: null; }; 'messaging/messages/read': { req: { messageId: MessagingMessage['id']; }; res: null; }; - 'meta': { req: { detail?: boolean; }; res: { - $switch: { - $cases: [[ - { detail: true; }, - DetailedInstanceMetadata, - ], [ - { detail: false; }, - LiteInstanceMetadata, - ], [ - { detail: boolean; }, - LiteInstanceMetadata | DetailedInstanceMetadata, - ]]; - $default: LiteInstanceMetadata; - }; - }; }; + 'meta': { req: { detail?: boolean; }; res: InstanceMetadata; }; 'miauth/gen-token': { req: TODO; res: TODO; }; 'mute/create': { req: TODO; res: TODO; }; 'mute/delete': { req: { userId: User['id'] }; res: null; }; diff --git a/packages/foundkey-js/src/entities.ts b/packages/foundkey-js/src/entities.ts index 3742630bb..d2cbe8289 100644 --- a/packages/foundkey-js/src/entities.ts +++ b/packages/foundkey-js/src/entities.ts @@ -260,7 +260,7 @@ export type CustomEmoji = { aliases: string[]; }; -export type LiteInstanceMetadata = { +export type InstanceMetadata = { maintainerName: string | null; maintainerEmail: string | null; version: string; @@ -290,14 +290,9 @@ export type LiteInstanceMetadata = { notFound: string; info: string; }; -}; - -export type DetailedInstanceMetadata = LiteInstanceMetadata & { features: Record; }; -export type InstanceMetadata = LiteInstanceMetadata | DetailedInstanceMetadata; - export type ServerInfo = { machine: string; cpu: { @@ -388,8 +383,6 @@ export type AuthSession = { token: string; }; -export type Ad = TODO; - export type Clip = TODO; export type NoteFavorite = { From 642c8bcca9f4f0ebdc8c86fdc7344fe386e5fe8d Mon Sep 17 00:00:00 2001 From: Johann150 Date: Sun, 7 May 2023 14:28:24 +0200 Subject: [PATCH 02/36] disable lint '@typescript-eslint/no-explicit-any' --- packages/shared/.eslintrc.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/shared/.eslintrc.js b/packages/shared/.eslintrc.js index 87748ad6f..f36e66a0b 100644 --- a/packages/shared/.eslintrc.js +++ b/packages/shared/.eslintrc.js @@ -67,6 +67,7 @@ module.exports = { '@typescript-eslint/no-var-requires': ['warn'], '@typescript-eslint/no-inferrable-types': ['warn'], '@typescript-eslint/no-empty-function': ['off'], + '@typescript-eslint/no-explicit-any': ['off'], '@typescript-eslint/no-non-null-assertion': ['warn'], '@typescript-eslint/explicit-function-return-type': ['warn', { allowExpressions: true }], '@typescript-eslint/no-misused-promises': ['error', { From a10b8875d936df6d21354e049e24a5e923c40aca Mon Sep 17 00:00:00 2001 From: Johann150 Date: Sun, 7 May 2023 15:26:14 +0200 Subject: [PATCH 03/36] run CI on pushes to main Since PRs seem to be much rarer than pushes to main, it makes more sense to do it this way. Perhaps in the future, it would make sense to run it on all pushes, but the question would be how PRs are handled. --- .woodpecker/build.yml | 8 ++++---- .woodpecker/lint-backend.yml | 8 ++++---- .woodpecker/lint-client.yml | 8 ++++---- .woodpecker/lint-foundkey-js.yml | 8 ++++---- .woodpecker/lint-sw.yml | 8 ++++---- .woodpecker/test.yml | 15 +++++++++------ 6 files changed, 29 insertions(+), 26 deletions(-) diff --git a/.woodpecker/build.yml b/.woodpecker/build.yml index e93473fe8..f4f2805c2 100644 --- a/.woodpecker/build.yml +++ b/.woodpecker/build.yml @@ -8,15 +8,15 @@ clone: pipeline: install: when: - event: - - pull_request + branch: main + event: push image: node:18.6.0 commands: - yarn install build: when: - event: - - pull_request + branch: main + event: push image: node:18.6.0 commands: - yarn build diff --git a/.woodpecker/lint-backend.yml b/.woodpecker/lint-backend.yml index 5690d99ae..096989cb7 100644 --- a/.woodpecker/lint-backend.yml +++ b/.woodpecker/lint-backend.yml @@ -8,15 +8,15 @@ clone: pipeline: install: when: - event: - - pull_request + branch: main + event: push image: node:18.6.0 commands: - yarn install lint: when: - event: - - pull_request + branch: main + event: push image: node:18.6.0 commands: - yarn workspace backend run lint diff --git a/.woodpecker/lint-client.yml b/.woodpecker/lint-client.yml index feb910081..6b60ac448 100644 --- a/.woodpecker/lint-client.yml +++ b/.woodpecker/lint-client.yml @@ -8,15 +8,15 @@ clone: pipeline: install: when: - event: - - pull_request + branch: main + event: push image: node:18.6.0 commands: - yarn install lint: when: - event: - - pull_request + branch: main + event: push image: node:18.6.0 commands: - yarn workspace client run lint diff --git a/.woodpecker/lint-foundkey-js.yml b/.woodpecker/lint-foundkey-js.yml index ce8162209..ddff1b46e 100644 --- a/.woodpecker/lint-foundkey-js.yml +++ b/.woodpecker/lint-foundkey-js.yml @@ -8,15 +8,15 @@ clone: pipeline: install: when: - event: - - pull_request + branch: main + event: push image: node:18.6.0 commands: - yarn install lint: when: - event: - - pull_request + branch: main + event: push image: node:18.6.0 commands: - yarn workspace foundkey-js run lint diff --git a/.woodpecker/lint-sw.yml b/.woodpecker/lint-sw.yml index 4a20add2e..aad1f4ad4 100644 --- a/.woodpecker/lint-sw.yml +++ b/.woodpecker/lint-sw.yml @@ -8,15 +8,15 @@ clone: pipeline: install: when: - event: - - pull_request + branch: main + event: push image: node:18.6.0 commands: - yarn install lint: when: - event: - - pull_request + branch: main + event: push image: node:18.6.0 commands: - yarn workspace sw run lint diff --git a/.woodpecker/test.yml b/.woodpecker/test.yml index 30eb04ee6..e42d68c73 100644 --- a/.woodpecker/test.yml +++ b/.woodpecker/test.yml @@ -5,11 +5,14 @@ clone: depth: 1 # CI does not need commit history recursive: true +depends_on: + - build + pipeline: build: when: - event: - - pull_request + branch: main + event: push image: node:18.6.0 commands: - yarn install @@ -18,15 +21,15 @@ pipeline: - yarn build mocha: when: - event: - - pull_request + branch: main + event: push image: node:18.6.0 commands: - yarn mocha e2e: when: - event: - - pull_request + branch: main + event: push image: cypress/included:10.3.0 commands: - npm run start:test & From 7e1ea094584d957be30ab2cf76268f2d2cb7bda3 Mon Sep 17 00:00:00 2001 From: Johann150 Date: Sun, 7 May 2023 17:12:04 +0200 Subject: [PATCH 04/36] fix some lints --- packages/client/src/account.ts | 100 +++++++++--------- packages/client/src/components/mfm.ts | 81 +++++++------- packages/client/src/directives/get-size.ts | 2 +- packages/client/src/directives/tooltip.ts | 6 +- packages/client/src/os.ts | 28 ++--- packages/client/src/pages/theme-editor.vue | 6 -- .../client/src/scripts/use-leave-guard.ts | 47 -------- packages/client/src/store.ts | 1 - packages/client/src/ui/_common_/sw-inject.ts | 2 - 9 files changed, 98 insertions(+), 175 deletions(-) delete mode 100644 packages/client/src/scripts/use-leave-guard.ts diff --git a/packages/client/src/account.ts b/packages/client/src/account.ts index e39540e47..491755373 100644 --- a/packages/client/src/account.ts +++ b/packages/client/src/account.ts @@ -3,9 +3,9 @@ import * as foundkey from 'foundkey-js'; import { showSuspendedDialog } from '@/scripts/show-suspended-dialog'; import { i18n } from '@/i18n'; import { del, get, set } from '@/scripts/idb-proxy'; -import { apiUrl } from '@/config'; import { waiting, api, popup, popupMenu, success, alert } from '@/os'; import { unisonReload, reloadChannel } from '@/scripts/unison-reload'; +import { MenuItem } from '@/types/menu'; // TODO: 他のタブと永続化されたstateを同期 @@ -22,7 +22,7 @@ export async function signout() { waiting(); localStorage.removeItem('account'); - await removeAccount($i.id); + if ($i) await removeAccount($i!.id); const accounts = await getAccounts(); @@ -99,14 +99,18 @@ function fetchAccount(token: string): Promise { } export function updateAccount(accountData) { + if (!$i) return; + for (const [key, value] of Object.entries(accountData)) { - $i[key] = value; + $i![key] = value; } - localStorage.setItem('account', JSON.stringify($i)); + localStorage.setItem('account', JSON.stringify($i!)); } export function refreshAccount() { - return fetchAccount($i.token).then(updateAccount); + if (!$i) return; + + return fetchAccount($i!.token).then(updateAccount); } export async function login(token: Account['token'], redirect?: string) { @@ -134,7 +138,39 @@ export async function openAccountMenu(opts: { active?: foundkey.entities.UserDetailed['id']; onChoose?: (account: foundkey.entities.UserDetailed) => void; }, ev: MouseEvent) { - function showSigninDialog() { + const storedAccounts = await getAccounts().then(accounts => accounts.filter(x => x.id !== $i?.id)); + const accountsPromise = api('users/show', { userIds: storedAccounts.map(x => x.id) }); + + const switchAccount = async (account: foundkey.entities.UserDetailed) => { + const storedAccounts = await getAccounts(); + const token = storedAccounts.find(x => x.id === account.id)?.token; + if (!token) { + // TODO error handling? + } else { + login(token); + } + }; + const createItem = (account: foundkey.entities.UserDetailed): MenuItem => ({ + type: 'user', + user: account, + active: opts.active != null ? opts.active === account.id : false, + action: () => { + if (opts.onChoose) { + opts.onChoose(account); + } else { + switchAccount(account); + } + }, + }); + const accountItemPromises: Promise = storedAccounts.map(a => new Promise(res => { + accountsPromise.then(accounts => { + const account = accounts.find(x => x.id === a.id); + if (account == null) return res(null); + res(createItem(account)); + }); + })); + + const showSigninDialog = () => { popup(defineAsyncComponent(() => import('@/components/signin-dialog.vue')), {}, { done: res => { addAccount(res.id, res.i); @@ -143,50 +179,14 @@ export async function openAccountMenu(opts: { }, 'closed'); } - function createAccount() { + const createAccount = () => { popup(defineAsyncComponent(() => import('@/components/signup-dialog.vue')), {}, { done: res => { addAccount(res.id, res.i); - switchAccountWithToken(res.i); + login(res.i); }, }, 'closed'); - } - - async function switchAccount(account: foundkey.entities.UserDetailed) { - const storedAccounts = await getAccounts(); - const token = storedAccounts.find(x => x.id === account.id).token; - switchAccountWithToken(token); - } - - function switchAccountWithToken(token: string) { - login(token); - } - - const storedAccounts = await getAccounts().then(accounts => accounts.filter(x => x.id !== $i.id)); - const accountsPromise = api('users/show', { userIds: storedAccounts.map(x => x.id) }); - - function createItem(account: foundkey.entities.UserDetailed) { - return { - type: 'user', - user: account, - active: opts.active != null ? opts.active === account.id : false, - action: () => { - if (opts.onChoose) { - opts.onChoose(account); - } else { - switchAccount(account); - } - }, - }; - } - - const accountItemPromises = storedAccounts.map(a => new Promise(res => { - accountsPromise.then(accounts => { - const account = accounts.find(x => x.id === a.id); - if (account == null) return res(null); - res(createItem(account)); - }); - })); + }; if (opts.withExtraOperation) { popupMenu([...[{ @@ -194,16 +194,16 @@ export async function openAccountMenu(opts: { text: i18n.ts.profile, to: `/@${ $i.username }`, avatar: $i, - }, null, ...(opts.includeCurrentAccount ? [createItem($i)] : []), ...accountItemPromises, { + }, null, ...(opts.includeCurrentAccount && $i ? [createItem($i)] : []), ...accountItemPromises, { icon: 'fas fa-plus', text: i18n.ts.addAccount, action: () => { popupMenu([{ text: i18n.ts.existingAccount, - action: () => { showSigninDialog(); }, + action: showSigninDialog, }, { text: i18n.ts.createAccount, - action: () => { createAccount(); }, + action: createAccount, }], ev.currentTarget ?? ev.target); }, }, { @@ -211,11 +211,11 @@ export async function openAccountMenu(opts: { icon: 'fas fa-users', text: i18n.ts.manageAccounts, to: '/settings/accounts', - }]], ev.currentTarget ?? ev.target, { + }]], ev.currentTarget ?? ev.target ?? undefined, { align: 'left', }); } else { - popupMenu([...(opts.includeCurrentAccount ? [createItem($i)] : []), ...accountItemPromises], ev.currentTarget ?? ev.target, { + popupMenu([...(opts.includeCurrentAccount && $i ? [createItem($i)] : []), ...accountItemPromises], ev.currentTarget ?? ev.target ?? undefined, { align: 'left', }); } diff --git a/packages/client/src/components/mfm.ts b/packages/client/src/components/mfm.ts index ef083eb4d..1107edcd6 100644 --- a/packages/client/src/components/mfm.ts +++ b/packages/client/src/components/mfm.ts @@ -44,18 +44,19 @@ export default defineComponent({ const ast = (this.plain ? mfm.parsePlain : mfm.parse)(this.text, { fnNameList: MFM_TAGS }); - const validTime = (t: string | null | undefined) => { - if (t == null) return null; + const validTime = (t: string | true) => { + if (typeof t !== 'string') return null; + return t.match(/^[0-9.]+s$/) ? t : null; }; - const genEl = (ast: mfm.MfmNode[]) => ast.map((token): VNode[] => { + const genEl = (ast: mfm.MfmNode[]) => ast.map((token): VNode | VNode[] => { switch (token.type) { case 'text': { const text = token.props.text.replace(/(\r\n|\n|\r)/g, '\n'); if (!this.plain) { - const res = []; + const res: VNode[] = []; for (const t of text.split('\n')) { res.push(h('br')); res.push(t); @@ -63,16 +64,16 @@ export default defineComponent({ res.shift(); return res; } else { - return [text.replace(/\n/g, ' ')]; + return text.replace(/\n/g, ' '); } } case 'bold': { - return [h('b', genEl(token.children))]; + return h('b', genEl(token.children)); } case 'strike': { - return [h('del', genEl(token.children))]; + return h('del', genEl(token.children)); } case 'italic': { @@ -180,7 +181,7 @@ export default defineComponent({ return h(MkSparkle, {}, genEl(token.children)); } case 'rotate': { - const degrees = parseInt(token.props.args.deg) || '90'; + const degrees = (typeof token.props.args.deg === 'string' ? parseInt(token.props.args.deg) : null) || '90'; style = `transform: rotate(${degrees}deg); transform-origin: center center;`; break; } @@ -195,116 +196,110 @@ export default defineComponent({ } case 'small': { - return [h('small', { + return h('small', { style: 'opacity: 0.7;', - }, genEl(token.children))]; + }, genEl(token.children)); } case 'center': { - return [h('div', { + return h('div', { style: 'text-align:center;', - }, genEl(token.children))]; + }, genEl(token.children)); } case 'url': { - return [h(MkUrl, { + return h(MkUrl, { key: Math.random(), url: token.props.url, rel: 'nofollow noopener', - })]; + }); } case 'link': { - return [h(MkLink, { + return h(MkLink, { key: Math.random(), url: token.props.url, rel: 'nofollow noopener', - }, genEl(token.children))]; + }, genEl(token.children)); } case 'mention': { - return [h(MkMention, { + return h(MkMention, { key: Math.random(), host: (token.props.host == null && this.author && this.author.host != null ? this.author.host : token.props.host) || host, username: token.props.username, - })]; + }); } case 'hashtag': { - return [h(MkA, { + return h(MkA, { key: Math.random(), to: this.isNote ? `/tags/${encodeURIComponent(token.props.hashtag)}` : `/explore/tags/${encodeURIComponent(token.props.hashtag)}`, style: 'color:var(--hashtag);', - }, `#${token.props.hashtag}`)]; + }, `#${token.props.hashtag}`); } case 'blockCode': { - return [h(MkCode, { + return h(MkCode, { key: Math.random(), code: token.props.code, lang: token.props.lang, - })]; + }); } case 'inlineCode': { - return [h(MkCode, { + return h(MkCode, { key: Math.random(), code: token.props.code, inline: true, - })]; + }); } case 'quote': { - if (!this.nowrap) { - return [h('div', { - class: 'quote', - }, genEl(token.children))]; - } else { - return [h('span', { - class: 'quote', - }, genEl(token.children))]; - } + return h(this.nowrap ? 'span' : 'div', { + class: 'quote', + }, genEl(token.children)); } case 'emojiCode': { - return [h(MkEmoji, { + return h(MkEmoji, { key: Math.random(), emoji: `:${token.props.name}:`, customEmojis: this.customEmojis, normal: this.plain, - })]; + }); } case 'unicodeEmoji': { - return [h(MkEmoji, { + return h(MkEmoji, { key: Math.random(), emoji: token.props.emoji, customEmojis: this.customEmojis, normal: this.plain, - })]; + }); } case 'mathInline': { - return [h(MkFormula, { + return h(MkFormula, { key: Math.random(), formula: token.props.formula, block: false, - })]; + }); } case 'mathBlock': { - return [h(MkFormula, { + return h(MkFormula, { key: Math.random(), formula: token.props.formula, block: true, - })]; + }); } case 'search': { - return [h(MkSearch, { + return h(MkSearch, { key: Math.random(), q: token.props.query, - })]; + }); } default: { diff --git a/packages/client/src/directives/get-size.ts b/packages/client/src/directives/get-size.ts index 575b4ce3c..74765ae03 100644 --- a/packages/client/src/directives/get-size.ts +++ b/packages/client/src/directives/get-size.ts @@ -43,7 +43,7 @@ export default { calc(src); }, - unmounted(src) { + unmounted(src, binding) { binding.value(0, 0); const info = mountings.get(src); if (!info) return; diff --git a/packages/client/src/directives/tooltip.ts b/packages/client/src/directives/tooltip.ts index 44511fb3e..80fb46b1f 100644 --- a/packages/client/src/directives/tooltip.ts +++ b/packages/client/src/directives/tooltip.ts @@ -10,7 +10,7 @@ class TooltipDirective { public text: string | null; private asMfm: boolean; - private _close: null | () => void; + private _close: null | (() => void); private showTimer: null | ReturnType; private hideTimer: null | ReturnType; @@ -23,7 +23,7 @@ class TooltipDirective { this.hideTimer = null; } - private close(): void { + public close(): void { if (this.hideTimer != null) return; // already closed or closing // cancel any pending attempts to show @@ -96,7 +96,7 @@ export default { const end = isTouchUsing ? 'touchend' : 'mouseleave'; el.addEventListener(start, () => self.show(el), { passive: true }); el.addEventListener(end, () => self.close(), { passive: true }); - el.addEventListener('click', self.close()); + el.addEventListener('click', () => self.close()); el.addEventListener('selectstart', ev => ev.preventDefault()); }, diff --git a/packages/client/src/os.ts b/packages/client/src/os.ts index 24f2659de..c465d3211 100644 --- a/packages/client/src/os.ts +++ b/packages/client/src/os.ts @@ -26,7 +26,7 @@ export const api = ((endpoint: string, data: Record = {}, token?: s const authorizationToken = token ?? $i?.token ?? undefined; const authorization = authorizationToken ? `Bearer ${authorizationToken}` : undefined; - const promise = new Promise((resolve, reject) => { + const promise = new Promise((resolve, reject) => { fetch(endpoint.indexOf('://') > -1 ? endpoint : `${apiUrl}/${endpoint}`, { method: 'POST', body: JSON.stringify(data), @@ -66,7 +66,7 @@ export const apiGet = ((endpoint: string, data: Record = {}, token? const authorizationToken = token ?? $i?.token ?? undefined; const authorization = authorizationToken ? `Bearer ${authorizationToken}` : undefined; - const promise = new Promise((resolve, reject) => { + const promise = new Promise((resolve, reject) => { // Send request fetch(`${apiUrl}/${endpoint}?${query}`, { method: 'GET', @@ -103,7 +103,7 @@ export const apiWithDialog = (( promiseDialog(promise, null, (err) => { alert({ type: 'error', - text: (err.message + '\n' + (err?.endpoint ?? '') + ' ' + (err?.code ?? '')).trim(), + text: (err.message + '\n' + (err.endpoint ?? '') + ' ' + (err.code ?? '')).trim(), }); }); @@ -293,9 +293,7 @@ export function inputDate(props: { text?: string | null; placeholder?: string | null; default?: Date | null; -}): Promise<{ canceled: true; result: undefined; } | { - canceled: false; result: Date; -}> { +}): Promise<{ canceled: true; } | { canceled: false; result: Date; }> { return new Promise((resolve) => { popup(defineAsyncComponent(() => import('@/components/dialog.vue')), { title: props.title, @@ -351,7 +349,7 @@ export function select(props: { } export function success() { - return new Promise((resolve) => { + return new Promise((resolve) => { const showing = ref(true); window.setTimeout(() => { showing.value = false; @@ -366,7 +364,7 @@ export function success() { } export function waiting() { - return new Promise((resolve) => { + return new Promise((resolve) => { const showing = ref(true); popup(defineAsyncComponent(() => import('@/components/waiting-dialog.vue')), { success: false, @@ -571,17 +569,3 @@ export function post(props: Record = {}) { } export const deckGlobalEvents = new EventEmitter(); - -/* -export function checkExistence(fileData: ArrayBuffer): Promise { - return new Promise((resolve) => { - const data = new FormData(); - data.append('md5', getMD5(fileData)); - - os.api('drive/files/find-by-hash', { - md5: getMD5(fileData) - }).then(resp => { - resolve(resp.length > 0 ? resp[0] : null); - }); - }); -}*/ diff --git a/packages/client/src/pages/theme-editor.vue b/packages/client/src/pages/theme-editor.vue index 4441c24dc..22688fab9 100644 --- a/packages/client/src/pages/theme-editor.vue +++ b/packages/client/src/pages/theme-editor.vue @@ -87,7 +87,6 @@ import * as os from '@/os'; import { ColdDeviceStorage, defaultStore } from '@/store'; import { addTheme } from '@/theme-store'; import { i18n } from '@/i18n'; -import { useLeaveGuard } from '@/scripts/use-leave-guard'; import { definePageMetadata } from '@/scripts/page-metadata'; const bgColors = [ @@ -125,9 +124,6 @@ let theme = $ref>({ }); let description = $ref(null); let themeCode = $ref(null); -let changed = $ref(false); - -useLeaveGuard($$(changed)); function showPreview() { os.pageWindow('/preview'); @@ -162,7 +158,6 @@ function setFgColor(color) { function apply() { themeCode = JSON5.stringify(theme, null, '\t'); applyTheme(theme, false); - changed = true; } function applyThemeCode() { @@ -199,7 +194,6 @@ async function saveAs() { } else { ColdDeviceStorage.set('lightTheme', theme); } - changed = false; os.alert({ type: 'success', text: i18n.t('_theme.installed', { name: theme.name }), diff --git a/packages/client/src/scripts/use-leave-guard.ts b/packages/client/src/scripts/use-leave-guard.ts deleted file mode 100644 index a93b84d1f..000000000 --- a/packages/client/src/scripts/use-leave-guard.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { inject, onUnmounted, Ref } from 'vue'; -import { i18n } from '@/i18n'; -import * as os from '@/os'; - -export function useLeaveGuard(enabled: Ref) { - /* TODO - const setLeaveGuard = inject('setLeaveGuard'); - - if (setLeaveGuard) { - setLeaveGuard(async () => { - if (!enabled.value) return false; - - const { canceled } = await os.confirm({ - type: 'warning', - text: i18n.ts.leaveConfirm, - }); - - return canceled; - }); - } else { - onBeforeRouteLeave(async (to, from) => { - if (!enabled.value) return true; - - const { canceled } = await os.confirm({ - type: 'warning', - text: i18n.ts.leaveConfirm, - }); - - return !canceled; - }); - } - */ - - /* - function onBeforeLeave(ev: BeforeUnloadEvent) { - if (enabled.value) { - ev.preventDefault(); - ev.returnValue = ''; - } - } - - window.addEventListener('beforeunload', onBeforeLeave); - onUnmounted(() => { - window.removeEventListener('beforeunload', onBeforeLeave); - }); - */ -} diff --git a/packages/client/src/store.ts b/packages/client/src/store.ts index 96ea2e725..489cca84d 100644 --- a/packages/client/src/store.ts +++ b/packages/client/src/store.ts @@ -1,6 +1,5 @@ import { markRaw, ref } from 'vue'; import { Storage } from '@/pizzax'; -import { Theme } from '@/scripts/theme'; export const postFormActions = []; export const userActions = []; diff --git a/packages/client/src/ui/_common_/sw-inject.ts b/packages/client/src/ui/_common_/sw-inject.ts index 8676d2d48..a92a06bd3 100644 --- a/packages/client/src/ui/_common_/sw-inject.ts +++ b/packages/client/src/ui/_common_/sw-inject.ts @@ -1,7 +1,5 @@ -import { inject } from 'vue'; import { post } from '@/os'; import { $i, login } from '@/account'; -import { defaultStore } from '@/store'; import { getAccountFromId } from '@/scripts/get-account-from-id'; import { mainRouter } from '@/router'; From 0df36490c7871fdcf06d6b1cee9bd092e5440837 Mon Sep 17 00:00:00 2001 From: Johann150 Date: Sun, 7 May 2023 18:07:18 +0200 Subject: [PATCH 05/36] fix lints in foundkey-js Fixes all lints except '@typescript-eslint/no-explicit-any'. --- packages/foundkey-js/src/api.ts | 12 +++++++----- packages/foundkey-js/src/entities.ts | 9 +++------ packages/foundkey-js/src/streaming.ts | 7 +++---- 3 files changed, 13 insertions(+), 15 deletions(-) diff --git a/packages/foundkey-js/src/api.ts b/packages/foundkey-js/src/api.ts index 923fbd304..8a9ff1fce 100644 --- a/packages/foundkey-js/src/api.ts +++ b/packages/foundkey-js/src/api.ts @@ -42,13 +42,15 @@ export class APIClient { constructor(opts: { origin: APIClient['origin']; credential?: APIClient['credential']; - fetch?: APIClient['fetch'] | null | undefined; + fetch?: FetchLike; }) { this.origin = opts.origin; this.credential = opts.credential; - // ネイティブ関数をそのまま変数に代入して使おうとするとChromiumではIllegal invocationエラーが発生するため、 - // 環境で実装されているfetchを使う場合は無名関数でラップして使用する - this.fetch = opts.fetch || ((...args) => fetch(...args)); + // Wrap a native function with an anonymous function when using + // environment-implemented fetch, because Chromium generates an + // Illegal invocation error if you try to use a native function by + // assigning it to a variable as it is. + this.fetch = opts.fetch || (((...args) => fetch(...args)) as FetchLike); } public request( @@ -79,7 +81,7 @@ export class APIClient { cache: 'no-cache', }).then(async (res) => { const body = res.status === 204 ? null : await res.json(); - + if (res.status === 200) { resolve(body); } else if (res.status === 204) { diff --git a/packages/foundkey-js/src/entities.ts b/packages/foundkey-js/src/entities.ts index d2cbe8289..5cec05c99 100644 --- a/packages/foundkey-js/src/entities.ts +++ b/packages/foundkey-js/src/entities.ts @@ -135,9 +135,9 @@ export type Note = { user: User; userId: User['id']; reply?: Note; - replyId: Note['id']; + replyId: Note['id'] | null; renote?: Note; - renoteId: Note['id']; + renoteId: Note['id'] | null; files: DriveFile[]; fileIds: DriveFile['id'][]; visibility: NoteVisibility; @@ -475,9 +475,6 @@ export function isPureRenote(note: Note): boolean { return note.renoteId != null && note.text == null && note.cw == null - && ( - note.fileIds == null - || note.fileIds.length === 0 - ) + && note.fileIds.length === 0 && note.poll == null; } diff --git a/packages/foundkey-js/src/streaming.ts b/packages/foundkey-js/src/streaming.ts index 0892db9d9..8f397f720 100644 --- a/packages/foundkey-js/src/streaming.ts +++ b/packages/foundkey-js/src/streaming.ts @@ -34,14 +34,13 @@ export default class Stream extends EventEmitter { constructor(origin: string, user: { token: string; } | null, options?: { WebSocket?: any; - }) { + } = {}) { super(); - options = options || { }; const query = urlQuery({ i: user?.token, - - // To prevent cache of an HTML such as error screen + + // cache busting parameter _t: Date.now(), }); From 683584fe8f87c490c53531b62873f909c54cb9b7 Mon Sep 17 00:00:00 2001 From: Johann150 Date: Sun, 7 May 2023 23:29:55 +0200 Subject: [PATCH 06/36] update mocha --- packages/backend/package.json | 2 +- yarn.lock | 18 +++++------------- 2 files changed, 6 insertions(+), 14 deletions(-) diff --git a/packages/backend/package.json b/packages/backend/package.json index 66afa14c3..5d7df7cfa 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -69,7 +69,7 @@ "koa-views": "7.0.2", "mfm-js": "0.22.1", "mime-types": "2.1.35", - "mocha": "10.0.0", + "mocha": "10.2.0", "multer": "1.4.5-lts.1", "nested-property": "4.0.0", "node-fetch": "3.2.6", diff --git a/yarn.lock b/yarn.lock index 0a679b247..8d225b0f4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2685,13 +2685,6 @@ __metadata: languageName: node linkType: hard -"@ungap/promise-all-settled@npm:1.1.2": - version: 1.1.2 - resolution: "@ungap/promise-all-settled@npm:1.1.2" - checksum: 08d37fdfa23a6fe8139f1305313562ebad973f3fac01bcce2773b2bda5bcb0146dfdcf3cb6a722cf0a5f2ca0bc56a827eac8f1e7b3beddc548f654addf1fc34c - languageName: node - linkType: hard - "@vitejs/plugin-vue@npm:^3.1.0": version: 3.1.0 resolution: "@vitejs/plugin-vue@npm:3.1.0" @@ -3767,7 +3760,7 @@ __metadata: koa-views: 7.0.2 mfm-js: 0.22.1 mime-types: 2.1.35 - mocha: 10.0.0 + mocha: 10.2.0 multer: 1.4.5-lts.1 nested-property: 4.0.0 node-fetch: 3.2.6 @@ -12012,11 +12005,10 @@ __metadata: languageName: node linkType: hard -"mocha@npm:10.0.0": - version: 10.0.0 - resolution: "mocha@npm:10.0.0" +"mocha@npm:10.2.0": + version: 10.2.0 + resolution: "mocha@npm:10.2.0" dependencies: - "@ungap/promise-all-settled": 1.1.2 ansi-colors: 4.1.1 browser-stdout: 1.3.1 chokidar: 3.5.3 @@ -12041,7 +12033,7 @@ __metadata: bin: _mocha: bin/_mocha mocha: bin/mocha.js - checksum: ba49ddcf8015a467e744b06c396aab361b1281302e38e7c1269af25ba51ff9ab681a9c36e9046bb7491e751cd7d5ce85e276a00ce7e204f96b2c418e4595edfe + checksum: 406c45eab122ffd6ea2003c2f108b2bc35ba036225eee78e0c784b6fa2c7f34e2b13f1dbacef55a4fdf523255d76e4f22d1b5aacda2394bd11666febec17c719 languageName: node linkType: hard From 605a55e1d48690436298380941967e7586ccfe06 Mon Sep 17 00:00:00 2001 From: Johann150 Date: Sun, 7 May 2023 23:32:23 +0200 Subject: [PATCH 07/36] tests: fix Resolver import --- packages/backend/test/misc/mock-resolver.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/backend/test/misc/mock-resolver.ts b/packages/backend/test/misc/mock-resolver.ts index ba89ac329..75b80e98c 100644 --- a/packages/backend/test/misc/mock-resolver.ts +++ b/packages/backend/test/misc/mock-resolver.ts @@ -1,4 +1,4 @@ -import Resolver from '../../src/remote/activitypub/resolver.js'; +import { Resolver } from '../../src/remote/activitypub/resolver.js'; import { IObject } from '../../src/remote/activitypub/type.js'; type MockResponse = { From eecff514c29c45400a022a811d494bf1959f8656 Mon Sep 17 00:00:00 2001 From: Johann150 Date: Sun, 7 May 2023 23:33:21 +0200 Subject: [PATCH 08/36] tests: use bearer authentication --- packages/backend/test/utils.ts | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/packages/backend/test/utils.ts b/packages/backend/test/utils.ts index a366547e6..59a62452c 100644 --- a/packages/backend/test/utils.ts +++ b/packages/backend/test/utils.ts @@ -31,16 +31,15 @@ export const async = (fn: Function) => (done: Function) => { export const api = async (endpoint: string, params: any, me?: any) => { endpoint = endpoint.replace(/^\//, ''); - const auth = me ? { - i: me.token - } : {}; + const auth = me ? { authorization: `Bearer ${me.token}` } : {}; const res = await got(`http://localhost:${port}/api/${endpoint}`, { method: 'POST', headers: { - 'Content-Type': 'application/json' + 'Content-Type': 'application/json', + ...auth, }, - body: JSON.stringify(Object.assign(auth, params)), + body: JSON.stringify(params), retry: { limit: 0, }, @@ -65,16 +64,15 @@ export const api = async (endpoint: string, params: any, me?: any) => { }; export const request = async (endpoint: string, params: any, me?: any): Promise<{ body: any, status: number }> => { - const auth = me ? { - i: me.token, - } : {}; + const auth = me ? { authorization: `Bearer ${me.token}` } : {}; const res = await fetch(`http://localhost:${port}/api${endpoint}`, { method: 'POST', headers: { 'Content-Type': 'application/json', + ...auth, }, - body: JSON.stringify(Object.assign(auth, params)), + body: JSON.stringify(params), }); const status = res.status; From 352851f23f5600e05cb403188ac8de8b83c41ec6 Mon Sep 17 00:00:00 2001 From: Johann150 Date: Sun, 7 May 2023 23:34:34 +0200 Subject: [PATCH 09/36] tests: fix error codes and HTTP statuses --- packages/backend/test/block.ts | 8 ++++---- packages/backend/test/note.ts | 10 +++++----- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/backend/test/block.ts b/packages/backend/test/block.ts index b3343813c..c35999d5c 100644 --- a/packages/backend/test/block.ts +++ b/packages/backend/test/block.ts @@ -35,7 +35,7 @@ describe('Block', () => { const res = await request('/following/create', { userId: alice.id }, bob); assert.strictEqual(res.status, 400); - assert.strictEqual(res.body.error.id, 'c4ab57cc-4e41-45e9-bfd9-584f61e35ce0'); + assert.strictEqual(res.body.error.code, 'BLOCKED'); })); it('ブロックされているユーザーにリアクションできない', async(async () => { @@ -44,7 +44,7 @@ describe('Block', () => { const res = await request('/notes/reactions/create', { noteId: note.id, reaction: '👍' }, bob); assert.strictEqual(res.status, 400); - assert.strictEqual(res.body.error.id, '20ef5475-9f38-4e4c-bd33-de6d979498ec'); + assert.strictEqual(res.body.error.code, 'BLOCKED'); })); it('ブロックされているユーザーに返信できない', async(async () => { @@ -53,7 +53,7 @@ describe('Block', () => { const res = await request('/notes/create', { replyId: note.id, text: 'yo' }, bob); assert.strictEqual(res.status, 400); - assert.strictEqual(res.body.error.id, 'b390d7e1-8a5e-46ed-b625-06271cafd3d3'); + assert.strictEqual(res.body.error.code, 'BLOCKED'); })); it('ブロックされているユーザーのノートをRenoteできない', async(async () => { @@ -62,7 +62,7 @@ describe('Block', () => { const res = await request('/notes/create', { renoteId: note.id, text: 'yo' }, bob); assert.strictEqual(res.status, 400); - assert.strictEqual(res.body.error.id, 'b390d7e1-8a5e-46ed-b625-06271cafd3d3'); + assert.strictEqual(res.body.error.code, 'BLOCKED'); })); // TODO: ユーザーリストに入れられないテスト diff --git a/packages/backend/test/note.ts b/packages/backend/test/note.ts index b495d8b7b..9cd5c650b 100644 --- a/packages/backend/test/note.ts +++ b/packages/backend/test/note.ts @@ -158,7 +158,7 @@ describe('Note', () => { replyId: '000000000000000000000000', }; const res = await request('/notes/create', post, alice); - assert.strictEqual(res.status, 400); + assert.strictEqual(res.status, 404); })); it('存在しないrenote対象で怒られる', async(async () => { @@ -166,7 +166,7 @@ describe('Note', () => { renoteId: '000000000000000000000000', }; const res = await request('/notes/create', post, alice); - assert.strictEqual(res.status, 400); + assert.strictEqual(res.status, 404); })); it('不正なリプライ先IDで怒られる', async(async () => { @@ -175,7 +175,7 @@ describe('Note', () => { replyId: 'foo', }; const res = await request('/notes/create', post, alice); - assert.strictEqual(res.status, 400); + assert.strictEqual(res.status, 404); })); it('不正なrenote対象IDで怒られる', async(async () => { @@ -183,7 +183,7 @@ describe('Note', () => { renoteId: 'foo', }; const res = await request('/notes/create', post, alice); - assert.strictEqual(res.status, 400); + assert.strictEqual(res.status, 404); })); it('存在しないユーザーにメンションできる', async(async () => { @@ -286,7 +286,7 @@ describe('Note', () => { choice: 2, }, alice); - assert.strictEqual(res.status, 400); + assert.strictEqual(res.status, 409); })); it('許可されている場合は複数投票できる', async(async () => { From 3d4df807b0e4465f3b23b2589e2f4472f78f8006 Mon Sep 17 00:00:00 2001 From: Johann150 Date: Sun, 7 May 2023 23:35:29 +0200 Subject: [PATCH 10/36] tests: refactor ffVisibility tests Using some closures to partly generate the tests to achieve better coverage --- packages/backend/test/ff-visibility.ts | 211 ++++++++++--------------- 1 file changed, 80 insertions(+), 131 deletions(-) diff --git a/packages/backend/test/ff-visibility.ts b/packages/backend/test/ff-visibility.ts index 4f6847be6..65feaba74 100644 --- a/packages/backend/test/ff-visibility.ts +++ b/packages/backend/test/ff-visibility.ts @@ -9,159 +9,108 @@ describe('FF visibility', () => { let alice: any; let bob: any; - let carol: any; + let follower: any; before(async () => { p = await startServer(); alice = await signup({ username: 'alice' }); bob = await signup({ username: 'bob' }); - carol = await signup({ username: 'carol' }); + follower = await signup({ username: 'follower' }); + + await request('/following/create', { userId: alice.id }, follower); }); after(async () => { await shutdownServer(p); }); - it('ffVisibility が public なユーザーのフォロー/フォロワーを誰でも見れる', async(async () => { - await request('/i/update', { - ffVisibility: 'public', - }, alice); + const visible = (user) => { + return async () => { + const followingRes = await request('/users/following', { + userId: alice.id, + }, user); + const followersRes = await request('/users/followers', { + userId: alice.id, + }, user); - const followingRes = await request('/users/following', { - userId: alice.id, - }, bob); - const followersRes = await request('/users/followers', { - userId: alice.id, - }, bob); + assert.strictEqual(followingRes.status, 200); + assert.ok(Array.isArray(followingRes.body)); + assert.strictEqual(followersRes.status, 200); + assert.ok(Array.isArray(followersRes.body)); + }; + }; - assert.strictEqual(followingRes.status, 200); - assert.strictEqual(Array.isArray(followingRes.body), true); - assert.strictEqual(followersRes.status, 200); - assert.strictEqual(Array.isArray(followersRes.body), true); - })); + const hidden = (user) => { + return async () => { + const followingRes = await request('/users/following', { + userId: alice.id, + }, user); + const followersRes = await request('/users/followers', { + userId: alice.id, + }, user); - it('ffVisibility が followers なユーザーのフォロー/フォロワーを自分で見れる', async(async () => { - await request('/i/update', { - ffVisibility: 'followers', - }, alice); + assert.strictEqual(followingRes.status, 403); + assert.strictEqual(followersRes.status, 403); + }; + }; - const followingRes = await request('/users/following', { - userId: alice.id, - }, alice); - const followersRes = await request('/users/followers', { - userId: alice.id, - }, alice); + describe('public visibility', () => { + before(async () => { + await request('/i/update', { + ffVisibility: 'public', + }, alice); + }); - assert.strictEqual(followingRes.status, 200); - assert.strictEqual(Array.isArray(followingRes.body), true); - assert.strictEqual(followersRes.status, 200); - assert.strictEqual(Array.isArray(followersRes.body), true); - })); + it('shows followers and following to self', visible(alice)); + it('shows followers and following to a follower', visible(follower)); + it('shows followers and following to a non-follower', visible(bob)); + it('shows followers and following when unauthenticated', visible(null)); - it('ffVisibility が followers なユーザーのフォロー/フォロワーを非フォロワーが見れない', async(async () => { - await request('/i/update', { - ffVisibility: 'followers', - }, alice); + it('provides followers in ActivityPub representation', async () => { + const followingRes = await simpleGet(`/users/${alice.id}/following`, 'application/activity+json'); + const followersRes = await simpleGet(`/users/${alice.id}/followers`, 'application/activity+json'); + assert.strictEqual(followingRes.status, 200); + assert.strictEqual(followersRes.status, 200); + }); + }); - const followingRes = await request('/users/following', { - userId: alice.id, - }, bob); - const followersRes = await request('/users/followers', { - userId: alice.id, - }, bob); + describe('followers visibility', () => { + before(async () => { + await request('/i/update', { + ffVisibility: 'followers', + }, alice); + }); - assert.strictEqual(followingRes.status, 400); - assert.strictEqual(followersRes.status, 400); - })); + it('shows followers and following to self', visible(alice)); + it('shows followers and following to a follower', visible(follower)); + it('hides followers and following from a non-follower', hidden(bob)); + it('hides followers and following when unauthenticated', hidden(null)); - it('ffVisibility が followers なユーザーのフォロー/フォロワーをフォロワーが見れる', async(async () => { - await request('/i/update', { - ffVisibility: 'followers', - }, alice); + it('hides followers from ActivityPub representation', async () => { + const followingRes = await simpleGet(`/users/${alice.id}/following`, 'application/activity+json').catch(res => ({ status: res.statusCode })); + const followersRes = await simpleGet(`/users/${alice.id}/followers`, 'application/activity+json').catch(res => ({ status: res.statusCode })); + assert.strictEqual(followingRes.status, 403); + assert.strictEqual(followersRes.status, 403); + }); + }); - await request('/following/create', { - userId: alice.id, - }, bob); + describe('private visibility', () => { + before(async () => { + await request('/i/update', { + ffVisibility: 'private', + }, alice); + }); - const followingRes = await request('/users/following', { - userId: alice.id, - }, bob); - const followersRes = await request('/users/followers', { - userId: alice.id, - }, bob); + it('shows followers and following to self', visible(alice)); + it('hides followers and following from a follower', hidden(follower)); + it('hides followers and following from a non-follower', hidden(bob)); + it('hides followers and following when unauthenticated', hidden(null)); - assert.strictEqual(followingRes.status, 200); - assert.strictEqual(Array.isArray(followingRes.body), true); - assert.strictEqual(followersRes.status, 200); - assert.strictEqual(Array.isArray(followersRes.body), true); - })); - - it('ffVisibility が private なユーザーのフォロー/フォロワーを自分で見れる', async(async () => { - await request('/i/update', { - ffVisibility: 'private', - }, alice); - - const followingRes = await request('/users/following', { - userId: alice.id, - }, alice); - const followersRes = await request('/users/followers', { - userId: alice.id, - }, alice); - - assert.strictEqual(followingRes.status, 200); - assert.strictEqual(Array.isArray(followingRes.body), true); - assert.strictEqual(followersRes.status, 200); - assert.strictEqual(Array.isArray(followersRes.body), true); - })); - - it('ffVisibility が private なユーザーのフォロー/フォロワーを他人が見れない', async(async () => { - await request('/i/update', { - ffVisibility: 'private', - }, alice); - - const followingRes = await request('/users/following', { - userId: alice.id, - }, bob); - const followersRes = await request('/users/followers', { - userId: alice.id, - }, bob); - - assert.strictEqual(followingRes.status, 400); - assert.strictEqual(followersRes.status, 400); - })); - - describe('AP', () => { - it('ffVisibility が public 以外ならばAPからは取得できない', async(async () => { - { - await request('/i/update', { - ffVisibility: 'public', - }, alice); - - const followingRes = await simpleGet(`/users/${alice.id}/following`, 'application/activity+json'); - const followersRes = await simpleGet(`/users/${alice.id}/followers`, 'application/activity+json'); - assert.strictEqual(followingRes.status, 200); - assert.strictEqual(followersRes.status, 200); - } - { - await request('/i/update', { - ffVisibility: 'followers', - }, alice); - - const followingRes = await simpleGet(`/users/${alice.id}/following`, 'application/activity+json').catch(res => ({ status: res.statusCode })); - const followersRes = await simpleGet(`/users/${alice.id}/followers`, 'application/activity+json').catch(res => ({ status: res.statusCode })); - assert.strictEqual(followingRes.status, 403); - assert.strictEqual(followersRes.status, 403); - } - { - await request('/i/update', { - ffVisibility: 'private', - }, alice); - - const followingRes = await simpleGet(`/users/${alice.id}/following`, 'application/activity+json').catch(res => ({ status: res.statusCode })); - const followersRes = await simpleGet(`/users/${alice.id}/followers`, 'application/activity+json').catch(res => ({ status: res.statusCode })); - assert.strictEqual(followingRes.status, 403); - assert.strictEqual(followersRes.status, 403); - } - })); + it('hides followers from ActivityPub representation', async () => { + const followingRes = await simpleGet(`/users/${alice.id}/following`, 'application/activity+json').catch(res => ({ status: res.statusCode })); + const followersRes = await simpleGet(`/users/${alice.id}/followers`, 'application/activity+json').catch(res => ({ status: res.statusCode })); + assert.strictEqual(followingRes.status, 403); + assert.strictEqual(followersRes.status, 403); + }); }); }); From d8c2cc2ef09c94f90b7496514d16e8131f97ec13 Mon Sep 17 00:00:00 2001 From: Johann150 Date: Sun, 7 May 2023 23:37:47 +0200 Subject: [PATCH 11/36] tests: translate some japanese to english --- packages/backend/test/api-visibility.ts | 146 ++++++++++++------------ packages/backend/test/block.ts | 16 +-- 2 files changed, 81 insertions(+), 81 deletions(-) diff --git a/packages/backend/test/api-visibility.ts b/packages/backend/test/api-visibility.ts index cde3cd2d0..3e453f8ee 100644 --- a/packages/backend/test/api-visibility.ts +++ b/packages/backend/test/api-visibility.ts @@ -17,15 +17,15 @@ describe('API visibility', () => { describe('Note visibility', async () => { //#region vars - /** ヒロイン */ + /** protagonist */ let alice: any; - /** フォロワー */ + /** follower */ let follower: any; - /** 非フォロワー */ + /** non-follower */ let other: any; - /** 非フォロワーでもリプライやメンションをされた人 */ + /** non-follower who has been replied to or mentioned */ let target: any; - /** specified mentionでmentionを飛ばされる人 */ + /** actor for which a specified visibility was set */ let target2: any; /** public-post */ @@ -100,90 +100,90 @@ describe('API visibility', () => { //#region show post // public - it('[show] public-postを自分が見れる', async(async () => { + it('[show] public post can be seen by author', async(async () => { const res = await show(pub.id, alice); assert.strictEqual(res.body.text, 'x'); })); - it('[show] public-postをフォロワーが見れる', async(async () => { + it('[show] public post can be seen by follower', async(async () => { const res = await show(pub.id, follower); assert.strictEqual(res.body.text, 'x'); })); - it('[show] public-postを非フォロワーが見れる', async(async () => { + it('[show] public post can be seen by non-follower', async(async () => { const res = await show(pub.id, other); assert.strictEqual(res.body.text, 'x'); })); - it('[show] public-postを未認証が見れる', async(async () => { + it('[show] public post can be seen unauthenticated', async(async () => { const res = await show(pub.id, null); assert.strictEqual(res.body.text, 'x'); })); // home - it('[show] home-postを自分が見れる', async(async () => { + it('[show] home post can be seen by author', async(async () => { const res = await show(home.id, alice); assert.strictEqual(res.body.text, 'x'); })); - it('[show] home-postをフォロワーが見れる', async(async () => { + it('[show] home post can be seen by follower', async(async () => { const res = await show(home.id, follower); assert.strictEqual(res.body.text, 'x'); })); - it('[show] home-postを非フォロワーが見れる', async(async () => { + it('[show] home post can be seen by non-follower', async(async () => { const res = await show(home.id, other); assert.strictEqual(res.body.text, 'x'); })); - it('[show] home-postを未認証が見れる', async(async () => { + it('[show] home post can be seen unauthenticated', async(async () => { const res = await show(home.id, null); assert.strictEqual(res.body.text, 'x'); })); // followers - it('[show] followers-postを自分が見れる', async(async () => { + it('[show] followers post can be seen by author', async(async () => { const res = await show(fol.id, alice); assert.strictEqual(res.body.text, 'x'); })); - it('[show] followers-postをフォロワーが見れる', async(async () => { + it('[show] followers post can be seen by follower', async(async () => { const res = await show(fol.id, follower); assert.strictEqual(res.body.text, 'x'); })); - it('[show] followers-postを非フォロワーが見れない', async(async () => { + it('[show] followers post is hidden from non-follower', async(async () => { const res = await show(fol.id, other); assert.strictEqual(res.status, 404); })); - it('[show] followers-postを未認証が見れない', async(async () => { + it('[show] followers post is hidden when unathenticated', async(async () => { const res = await show(fol.id, null); assert.strictEqual(res.status, 404); })); // specified - it('[show] specified-postを自分が見れる', async(async () => { + it('[show] specified post can be seen by author', async(async () => { const res = await show(spe.id, alice); assert.strictEqual(res.status, 404); })); - it('[show] specified-postを指定ユーザーが見れる', async(async () => { + it('[show] specified post can be seen by designated user', async(async () => { const res = await show(spe.id, target); assert.strictEqual(res.body.text, 'x'); })); - it('[show] specified-postをフォロワーが見れない', async(async () => { + it('[show] specified post is hidden from non-specified follower', async(async () => { const res = await show(spe.id, follower); assert.strictEqual(res.status, 404); })); - it('[show] specified-postを非フォロワーが見れない', async(async () => { + it('[show] specified post is hidden from non-follower', async(async () => { const res = await show(spe.id, other); assert.strictEqual(res.status, 404); })); - it('[show] specified-postを未認証が見れない', async(async () => { + it('[show] specified post is hidden when unauthenticated', async(async () => { const res = await show(spe.id, null); assert.strictEqual(res.status, 404); })); @@ -191,90 +191,90 @@ describe('API visibility', () => { //#region show reply // public - it('[show] public-replyを自分が見れる', async(async () => { + it('[show] public reply can be seen by author', async(async () => { const res = await show(pubR.id, alice); assert.strictEqual(res.body.text, 'x'); })); - it('[show] public-replyをされた人が見れる', async(async () => { + it('[show] public reply can be seen by replied to author', async(async () => { const res = await show(pubR.id, target); assert.strictEqual(res.body.text, 'x'); })); - it('[show] public-replyをフォロワーが見れる', async(async () => { + it('[show] public reply can be seen by follower', async(async () => { const res = await show(pubR.id, follower); assert.strictEqual(res.body.text, 'x'); })); - it('[show] public-replyを非フォロワーが見れる', async(async () => { + it('[show] public reply can be seen by non-follower', async(async () => { const res = await show(pubR.id, other); assert.strictEqual(res.body.text, 'x'); })); - it('[show] public-replyを未認証が見れる', async(async () => { + it('[show] public reply can be seen unauthenticated', async(async () => { const res = await show(pubR.id, null); assert.strictEqual(res.body.text, 'x'); })); // home - it('[show] home-replyを自分が見れる', async(async () => { + it('[show] home reply can be seen by author', async(async () => { const res = await show(homeR.id, alice); assert.strictEqual(res.body.text, 'x'); })); - it('[show] home-replyをされた人が見れる', async(async () => { + it('[show] home reply can be seen by replied to author', async(async () => { const res = await show(homeR.id, target); assert.strictEqual(res.body.text, 'x'); })); - it('[show] home-replyをフォロワーが見れる', async(async () => { + it('[show] home reply can be seen by follower', async(async () => { const res = await show(homeR.id, follower); assert.strictEqual(res.body.text, 'x'); })); - it('[show] home-replyを非フォロワーが見れる', async(async () => { + it('[show] home reply can be seen by non-follower', async(async () => { const res = await show(homeR.id, other); assert.strictEqual(res.body.text, 'x'); })); - it('[show] home-replyを未認証が見れる', async(async () => { + it('[show] home reply can be seen unauthenticated', async(async () => { const res = await show(homeR.id, null); assert.strictEqual(res.body.text, 'x'); })); // followers - it('[show] followers-replyを自分が見れる', async(async () => { + it('[show] followers reply can be seen by author', async(async () => { const res = await show(folR.id, alice); assert.strictEqual(res.body.text, 'x'); })); - it('[show] followers-replyを非フォロワーでもリプライされていれば見れる', async(async () => { + it('[show] followers reply can be seen by replied to author', async(async () => { const res = await show(folR.id, target); assert.strictEqual(res.body.text, 'x'); })); - it('[show] followers-replyをフォロワーが見れる', async(async () => { + it('[show] followers reply can be seen by follower', async(async () => { const res = await show(folR.id, follower); assert.strictEqual(res.body.text, 'x'); })); - it('[show] followers-replyを非フォロワーが見れない', async(async () => { + it('[show] followers reply is hidden from non-follower', async(async () => { const res = await show(folR.id, other); assert.strictEqual(res.status, 404); })); - it('[show] followers-replyを未認証が見れない', async(async () => { + it('[show] followers reply is hidden when unauthenticated', async(async () => { const res = await show(folR.id, null); assert.strictEqual(res.status, 404); })); // specified - it('[show] specified-replyを自分が見れる', async(async () => { + it('[show] specified reply can be seen by author', async(async () => { const res = await show(speR.id, alice); assert.strictEqual(res.body.text, 'x'); })); - it('[show] specified-replyを指定ユーザーが見れる', async(async () => { + it('[show] specified reply can be seen by replied to user', async(async () => { const res = await show(speR.id, target); assert.strictEqual(res.body.text, 'x'); })); @@ -284,17 +284,17 @@ describe('API visibility', () => { assert.strictEqual(res.body.text, 'x'); })); - it('[show] specified-replyをフォロワーが見れない', async(async () => { + it('[show] specified reply is hidden from follower', async(async () => { const res = await show(speR.id, follower); assert.strictEqual(res.status, 404); })); - it('[show] specified-replyを非フォロワーが見れない', async(async () => { + it('[show] specified reply is hidden from non-follower', async(async () => { const res = await show(speR.id, other); assert.strictEqual(res.status, 404); })); - it('[show] specified-replyを未認証が見れない', async(async () => { + it('[show] specified reply is hidden when unauthenticated', async(async () => { const res = await show(speR.id, null); assert.strictEqual(res.status, 404); })); @@ -302,131 +302,131 @@ describe('API visibility', () => { //#region show mention // public - it('[show] public-mentionを自分が見れる', async(async () => { + it('[show] public-mention can be seen by author', async(async () => { const res = await show(pubM.id, alice); assert.strictEqual(res.body.text, '@target x'); })); - it('[show] public-mentionをされた人が見れる', async(async () => { + it('[show] public mention can be seen by mentioned', async(async () => { const res = await show(pubM.id, target); assert.strictEqual(res.body.text, '@target x'); })); - it('[show] public-mentionをフォロワーが見れる', async(async () => { + it('[show] public mention can be seen by follower', async(async () => { const res = await show(pubM.id, follower); assert.strictEqual(res.body.text, '@target x'); })); - it('[show] public-mentionを非フォロワーが見れる', async(async () => { + it('[show] public mention can be seen by non-follower', async(async () => { const res = await show(pubM.id, other); assert.strictEqual(res.body.text, '@target x'); })); - it('[show] public-mentionを未認証が見れる', async(async () => { + it('[show] public mention can be seen unauthenticated', async(async () => { const res = await show(pubM.id, null); assert.strictEqual(res.body.text, '@target x'); })); // home - it('[show] home-mentionを自分が見れる', async(async () => { + it('[show] home mention can be seen by author', async(async () => { const res = await show(homeM.id, alice); assert.strictEqual(res.body.text, '@target x'); })); - it('[show] home-mentionをされた人が見れる', async(async () => { + it('[show] home mention can be seen by mentioned', async(async () => { const res = await show(homeM.id, target); assert.strictEqual(res.body.text, '@target x'); })); - it('[show] home-mentionをフォロワーが見れる', async(async () => { + it('[show] home mention can be seen by follower', async(async () => { const res = await show(homeM.id, follower); assert.strictEqual(res.body.text, '@target x'); })); - it('[show] home-mentionを非フォロワーが見れる', async(async () => { + it('[show] home mention can be seen by non-follower', async(async () => { const res = await show(homeM.id, other); assert.strictEqual(res.body.text, '@target x'); })); - it('[show] home-mentionを未認証が見れる', async(async () => { + it('[show] home mention can be seen unauthenticated', async(async () => { const res = await show(homeM.id, null); assert.strictEqual(res.body.text, '@target x'); })); // followers - it('[show] followers-mentionを自分が見れる', async(async () => { + it('[show] followers mention can be seen by author', async(async () => { const res = await show(folM.id, alice); assert.strictEqual(res.body.text, '@target x'); })); - it('[show] followers-mentionをメンションされていれば非フォロワーでも見れる', async(async () => { + it('[show] followers mention can be seen by non-follower mentioned', async(async () => { const res = await show(folM.id, target); assert.strictEqual(res.body.text, '@target x'); })); - it('[show] followers-mentionをフォロワーが見れる', async(async () => { + it('[show] followers mention can be seen by follower', async(async () => { const res = await show(folM.id, follower); assert.strictEqual(res.body.text, '@target x'); })); - it('[show] followers-mentionを非フォロワーが見れない', async(async () => { + it('[show] followers mention is hidden from non-follower', async(async () => { const res = await show(folM.id, other); assert.strictEqual(res.status, 404); })); - it('[show] followers-mentionを未認証が見れない', async(async () => { + it('[show] followers mention is hidden when unauthenticated', async(async () => { const res = await show(folM.id, null); assert.strictEqual(res.status, 404); })); // specified - it('[show] specified-mentionを自分が見れる', async(async () => { + it('[show] specified mention can be seen by author', async(async () => { const res = await show(speM.id, alice); assert.strictEqual(res.body.text, '@target2 x'); })); - it('[show] specified-mentionを指定ユーザーが見れる', async(async () => { + it('[show] specified mention can be seen by specified actor', async(async () => { const res = await show(speM.id, target); assert.strictEqual(res.body.text, '@target2 x'); })); - it('[show] specified-mentionをされた人が指定されてなかったら見れない', async(async () => { + it('[show] specified mention is hidden from mentioned but not specified actor', async(async () => { const res = await show(speM.id, target2); assert.strictEqual(res.status, 404); })); - it('[show] specified-mentionをフォロワーが見れない', async(async () => { + it('[show] specified mention is hidden from follower', async(async () => { const res = await show(speM.id, follower); assert.strictEqual(res.status, 404); })); - it('[show] specified-mentionを非フォロワーが見れない', async(async () => { + it('[show] specified mention is hidden from non-follower', async(async () => { const res = await show(speM.id, other); assert.strictEqual(res.status, 404); })); - it('[show] specified-mentionを未認証が見れない', async(async () => { + it('[show] specified mention is hidden when unauthenticated', async(async () => { const res = await show(speM.id, null); assert.strictEqual(res.status, 404); })); //#endregion - //#region HTL - it('[HTL] public-post が 自分が見れる', async(async () => { + //#region Home Timeline + it('[TL] public post on author home TL', async(async () => { const res = await request('/notes/timeline', { limit: 100 }, alice); assert.strictEqual(res.status, 200); const notes = res.body.filter((n: any) => n.id == pub.id); assert.strictEqual(notes[0].text, 'x'); })); - it('[HTL] public-post が 非フォロワーから見れない', async(async () => { + it('[TL] public post absent from non-follower home TL', async(async () => { const res = await request('/notes/timeline', { limit: 100 }, other); assert.strictEqual(res.status, 200); const notes = res.body.filter((n: any) => n.id == pub.id); assert.strictEqual(notes.length, 0); })); - it('[HTL] followers-post が フォロワーから見れる', async(async () => { + it('[TL] followers post on follower home TL', async(async () => { const res = await request('/notes/timeline', { limit: 100 }, follower); assert.strictEqual(res.status, 200); const notes = res.body.filter((n: any) => n.id == fol.id); @@ -434,22 +434,22 @@ describe('API visibility', () => { })); //#endregion - //#region RTL - it('[replies] followers-reply が フォロワーから見れる', async(async () => { + //#region replies timeline + it('[TL] followers reply on follower reply TL', async(async () => { const res = await request('/notes/replies', { noteId: tgt.id, limit: 100 }, follower); assert.strictEqual(res.status, 200); const notes = res.body.filter((n: any) => n.id == folR.id); assert.strictEqual(notes[0].text, 'x'); })); - it('[replies] followers-reply が 非フォロワー (リプライ先ではない) から見れない', async(async () => { + it('[TL] followers reply absent from not replied to non-follower reply TL', async(async () => { const res = await request('/notes/replies', { noteId: tgt.id, limit: 100 }, other); assert.strictEqual(res.status, 200); const notes = res.body.filter((n: any) => n.id == folR.id); assert.strictEqual(notes.length, 0); })); - it('[replies] followers-reply が 非フォロワー (リプライ先である) から見れる', async(async () => { + it('[TL] followers reply on replied to actor reply TL', async(async () => { const res = await request('/notes/replies', { noteId: tgt.id, limit: 100 }, target); assert.strictEqual(res.status, 200); const notes = res.body.filter((n: any) => n.id == folR.id); @@ -458,14 +458,14 @@ describe('API visibility', () => { //#endregion //#region MTL - it('[mentions] followers-reply が 非フォロワー (リプライ先である) から見れる', async(async () => { + it('[TL] followers reply on replied to non-follower mention TL', async(async () => { const res = await request('/notes/mentions', { limit: 100 }, target); assert.strictEqual(res.status, 200); const notes = res.body.filter((n: any) => n.id == folR.id); assert.strictEqual(notes[0].text, 'x'); })); - it('[mentions] followers-mention が 非フォロワー (メンション先である) から見れる', async(async () => { + it('[TL] followers mention on mentioned non-follower mention TL', async(async () => { const res = await request('/notes/mentions', { limit: 100 }, target); assert.strictEqual(res.status, 200); const notes = res.body.filter((n: any) => n.id == folM.id); diff --git a/packages/backend/test/block.ts b/packages/backend/test/block.ts index c35999d5c..2bcfbcb6e 100644 --- a/packages/backend/test/block.ts +++ b/packages/backend/test/block.ts @@ -23,7 +23,7 @@ describe('Block', () => { await shutdownServer(p); }); - it('Block作成', async(async () => { + it('can block someone', async(async () => { const res = await request('/blocking/create', { userId: bob.id, }, alice); @@ -31,14 +31,14 @@ describe('Block', () => { assert.strictEqual(res.status, 200); })); - it('ブロックされているユーザーをフォローできない', async(async () => { + it('cannot follow if blocked', async(async () => { const res = await request('/following/create', { userId: alice.id }, bob); assert.strictEqual(res.status, 400); assert.strictEqual(res.body.error.code, 'BLOCKED'); })); - it('ブロックされているユーザーにリアクションできない', async(async () => { + it('cannot react to blocking users note', async(async () => { const note = await post(alice, { text: 'hello' }); const res = await request('/notes/reactions/create', { noteId: note.id, reaction: '👍' }, bob); @@ -47,7 +47,7 @@ describe('Block', () => { assert.strictEqual(res.body.error.code, 'BLOCKED'); })); - it('ブロックされているユーザーに返信できない', async(async () => { + it('cannot reply to blocking users note', async(async () => { const note = await post(alice, { text: 'hello' }); const res = await request('/notes/create', { replyId: note.id, text: 'yo' }, bob); @@ -56,7 +56,7 @@ describe('Block', () => { assert.strictEqual(res.body.error.code, 'BLOCKED'); })); - it('ブロックされているユーザーのノートをRenoteできない', async(async () => { + it('canot renote blocking users note', async(async () => { const note = await post(alice, { text: 'hello' }); const res = await request('/notes/create', { renoteId: note.id, text: 'yo' }, bob); @@ -65,11 +65,11 @@ describe('Block', () => { assert.strictEqual(res.body.error.code, 'BLOCKED'); })); - // TODO: ユーザーリストに入れられないテスト + // TODO: test that blocked user cannot be included in user list - // TODO: ユーザーリストから除外されるテスト + // TODO: test that blocked user is removed from user list - it('タイムライン(LTL)にブロックされているユーザーの投稿が含まれない', async(async () => { + it('local timeline does not contain blocked users', async(async () => { const aliceNote = await post(alice); const bobNote = await post(bob); const carolNote = await post(carol); From 5a4e300552d0463873d0838c523ef6e5641822ca Mon Sep 17 00:00:00 2001 From: Johann150 Date: Sun, 7 May 2023 23:38:29 +0200 Subject: [PATCH 12/36] tests: fix visibility tests removed a duplicate test and fixed another that i messed up when updating tests for the changed visibility --- packages/backend/test/api-visibility.ts | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/packages/backend/test/api-visibility.ts b/packages/backend/test/api-visibility.ts index 3e453f8ee..0cde9ec35 100644 --- a/packages/backend/test/api-visibility.ts +++ b/packages/backend/test/api-visibility.ts @@ -165,7 +165,7 @@ describe('API visibility', () => { // specified it('[show] specified post can be seen by author', async(async () => { const res = await show(spe.id, alice); - assert.strictEqual(res.status, 404); + assert.strictEqual(res.body.text, 'x'); })); it('[show] specified post can be seen by designated user', async(async () => { @@ -279,11 +279,6 @@ describe('API visibility', () => { assert.strictEqual(res.body.text, 'x'); })); - it('[show] specified-replyをされた人が指定されてなくても見れる', async(async () => { - const res = await show(speR.id, target); - assert.strictEqual(res.body.text, 'x'); - })); - it('[show] specified reply is hidden from follower', async(async () => { const res = await show(speR.id, follower); assert.strictEqual(res.status, 404); From 2e32715d36cde1dec00e7b5c662c7bce7ea28e2f Mon Sep 17 00:00:00 2001 From: Johann150 Date: Sun, 7 May 2023 23:42:51 +0200 Subject: [PATCH 13/36] fix typescript error TS1015 --- packages/foundkey-js/src/streaming.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/foundkey-js/src/streaming.ts b/packages/foundkey-js/src/streaming.ts index 8f397f720..4a2ca58e6 100644 --- a/packages/foundkey-js/src/streaming.ts +++ b/packages/foundkey-js/src/streaming.ts @@ -32,7 +32,7 @@ export default class Stream extends EventEmitter { private nonSharedConnections: NonSharedConnection[] = []; private idCounter = 0; - constructor(origin: string, user: { token: string; } | null, options?: { + constructor(origin: string, user: { token: string; } | null, options: { WebSocket?: any; } = {}) { super(); From ac81acfe9d5ecab133c400ccfa9ec201d128266d Mon Sep 17 00:00:00 2001 From: Johann150 Date: Mon, 8 May 2023 00:07:40 +0200 Subject: [PATCH 14/36] tests: disable timeouts for server setup --- packages/backend/test/api-visibility.ts | 1 + packages/backend/test/api.ts | 1 + packages/backend/test/block.ts | 6 ++++-- packages/backend/test/fetch-resource.ts | 1 + packages/backend/test/ff-visibility.ts | 2 ++ packages/backend/test/mute.ts | 2 ++ packages/backend/test/note.ts | 2 ++ packages/backend/test/services/blocking.ts | 2 ++ packages/backend/test/streaming.ts | 2 ++ packages/backend/test/thread-mute.ts | 2 ++ packages/backend/test/user-notes.ts | 2 ++ 11 files changed, 21 insertions(+), 2 deletions(-) diff --git a/packages/backend/test/api-visibility.ts b/packages/backend/test/api-visibility.ts index 0cde9ec35..6f1d51a67 100644 --- a/packages/backend/test/api-visibility.ts +++ b/packages/backend/test/api-visibility.ts @@ -8,6 +8,7 @@ describe('API visibility', () => { let p: childProcess.ChildProcess; before(async () => { + this.timeout(0); p = await startServer(); }); diff --git a/packages/backend/test/api.ts b/packages/backend/test/api.ts index b1b2ecafc..ae46ae92d 100644 --- a/packages/backend/test/api.ts +++ b/packages/backend/test/api.ts @@ -11,6 +11,7 @@ describe('API', () => { let carol: any; before(async () => { + this.timeout(0); p = await startServer(); alice = await signup({ username: 'alice' }); bob = await signup({ username: 'bob' }); diff --git a/packages/backend/test/block.ts b/packages/backend/test/block.ts index 2bcfbcb6e..ec5d54ca0 100644 --- a/packages/backend/test/block.ts +++ b/packages/backend/test/block.ts @@ -13,6 +13,8 @@ describe('Block', () => { let carol: any; before(async () => { + this.timeout(0); + p = await startServer(); alice = await signup({ username: 'alice' }); bob = await signup({ username: 'bob' }); @@ -65,9 +67,9 @@ describe('Block', () => { assert.strictEqual(res.body.error.code, 'BLOCKED'); })); - // TODO: test that blocked user cannot be included in user list + it('cannot include blocked users in user lists'); - // TODO: test that blocked user is removed from user list + it('removes users from user lists'); it('local timeline does not contain blocked users', async(async () => { const aliceNote = await post(alice); diff --git a/packages/backend/test/fetch-resource.ts b/packages/backend/test/fetch-resource.ts index ddb0e94b8..2bdd7a7e2 100644 --- a/packages/backend/test/fetch-resource.ts +++ b/packages/backend/test/fetch-resource.ts @@ -23,6 +23,7 @@ describe('Fetch resource', () => { let alicesPost: any; before(async () => { + this.timeout(0); p = await startServer(); alice = await signup({ username: 'alice' }); alicesPost = await post(alice, { diff --git a/packages/backend/test/ff-visibility.ts b/packages/backend/test/ff-visibility.ts index 65feaba74..59b94e135 100644 --- a/packages/backend/test/ff-visibility.ts +++ b/packages/backend/test/ff-visibility.ts @@ -12,6 +12,8 @@ describe('FF visibility', () => { let follower: any; before(async () => { + this.timeout(0); + p = await startServer(); alice = await signup({ username: 'alice' }); bob = await signup({ username: 'bob' }); diff --git a/packages/backend/test/mute.ts b/packages/backend/test/mute.ts index 465633973..602623f4b 100644 --- a/packages/backend/test/mute.ts +++ b/packages/backend/test/mute.ts @@ -13,6 +13,8 @@ describe('Mute', () => { let carol: any; before(async () => { + this.timeout(0); + p = await startServer(); alice = await signup({ username: 'alice' }); bob = await signup({ username: 'bob' }); diff --git a/packages/backend/test/note.ts b/packages/backend/test/note.ts index 9cd5c650b..1f7cf8f03 100644 --- a/packages/backend/test/note.ts +++ b/packages/backend/test/note.ts @@ -13,6 +13,8 @@ describe('Note', () => { let bob: any; before(async () => { + this.timeout(0); + p = await startServer(); const connection = await initTestDb(true); Notes = connection.getRepository(Note); diff --git a/packages/backend/test/services/blocking.ts b/packages/backend/test/services/blocking.ts index 41e5ef5be..2f72972f2 100644 --- a/packages/backend/test/services/blocking.ts +++ b/packages/backend/test/services/blocking.ts @@ -14,6 +14,8 @@ describe('Creating a block activity', () => { let carol: any; before(async () => { + this.timeout(0); + await initTestDb(); p = await startServer(); alice = await signup({ username: 'alice' }); diff --git a/packages/backend/test/streaming.ts b/packages/backend/test/streaming.ts index 621d07f9c..dd0e814d4 100644 --- a/packages/backend/test/streaming.ts +++ b/packages/backend/test/streaming.ts @@ -38,6 +38,8 @@ describe('Streaming', () => { let list: any; before(async () => { + this.timeout(0); + p = await startServer(); const connection = await initTestDb(true); Followings = connection.getRepository(Following); diff --git a/packages/backend/test/thread-mute.ts b/packages/backend/test/thread-mute.ts index cd3e51939..d1338fd3b 100644 --- a/packages/backend/test/thread-mute.ts +++ b/packages/backend/test/thread-mute.ts @@ -12,6 +12,8 @@ describe('Note thread mute', () => { let carol: any; before(async () => { + this.timeout(0); + p = await startServer(); alice = await signup({ username: 'alice' }); bob = await signup({ username: 'bob' }); diff --git a/packages/backend/test/user-notes.ts b/packages/backend/test/user-notes.ts index 4447754d6..31a66dbd2 100644 --- a/packages/backend/test/user-notes.ts +++ b/packages/backend/test/user-notes.ts @@ -13,6 +13,8 @@ describe('users/notes', () => { let jpgPngNote: any; before(async () => { + this.timeout(0); + p = await startServer(); alice = await signup({ username: 'alice' }); const jpg = await uploadUrl(alice, 'https://raw.githubusercontent.com/misskey-dev/misskey/develop/packages/backend/test/resources/Lenna.jpg'); From e6c7f4b6934afcca27a87da9598d869f4089d81a Mon Sep 17 00:00:00 2001 From: Johann150 Date: Fri, 12 May 2023 19:18:17 +0200 Subject: [PATCH 15/36] refactor: use switch for receiving IPC messages --- packages/backend/src/boot/master.ts | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/packages/backend/src/boot/master.ts b/packages/backend/src/boot/master.ts index 4e5327641..a688a8ae6 100644 --- a/packages/backend/src/boot/master.ts +++ b/packages/backend/src/boot/master.ts @@ -160,12 +160,15 @@ function spawnWorker(mode: 'web' | 'queue'): Promise { return new Promise(res => { const worker = cluster.fork({ mode }); worker.on('message', message => { - if (message === 'listenFailed') { - bootLogger.error('The server Listen failed due to the previous error.'); - process.exit(1); + switch (message) { + case 'listenFailed': + bootLogger.error('The server Listen failed due to the previous error.'); + process.exit(1); + break; + case 'ready': + res(); + break; } - if (message !== 'ready') return; - res(); }); }); } From 386d1bbf7462d98c9f4663a5d54f4a5fdd39931f Mon Sep 17 00:00:00 2001 From: Johann150 Date: Fri, 12 May 2023 19:34:08 +0200 Subject: [PATCH 16/36] fix: update meta cache between workers --- packages/backend/src/boot/master.ts | 9 +++++++++ packages/backend/src/misc/fetch-meta.ts | 12 ++++++++++++ 2 files changed, 21 insertions(+) diff --git a/packages/backend/src/boot/master.ts b/packages/backend/src/boot/master.ts index a688a8ae6..6f64715f8 100644 --- a/packages/backend/src/boot/master.ts +++ b/packages/backend/src/boot/master.ts @@ -168,6 +168,15 @@ function spawnWorker(mode: 'web' | 'queue'): Promise { case 'ready': res(); break; + case 'metaUpdate': + // forward new instance metadata to all workers + for (const otherWorker of Object.values(cluster.workers)) { + // don't forward the message to the worker that sent it + if (worker.id === otherWorker.id) continue; + + otherWorker.send(message); + } + break; } }); }); diff --git a/packages/backend/src/misc/fetch-meta.ts b/packages/backend/src/misc/fetch-meta.ts index ab8c81eef..da71b1d4b 100644 --- a/packages/backend/src/misc/fetch-meta.ts +++ b/packages/backend/src/misc/fetch-meta.ts @@ -1,3 +1,4 @@ +import process from 'node:process'; import push from 'web-push'; import { db } from '@/db/postgre.js'; import { Meta } from '@/models/entities/meta.js'; @@ -17,9 +18,20 @@ export async function setMeta(meta: Meta): Promise { cache = meta; + /* + The meta is not included here because another process may have updated + the content before the other process receives it. + */ + process.send!('metaUpdated'); + unlock(); } +// the primary will forward this message +process.on('message', async message => { + if (message === 'metaUpdated') await getMeta(); +}); + /** * Performs the primitive database operation to fetch server configuration. * If there is no entry yet, inserts a new one. From afd6076c1fc1ec157d9c8180e9e61aa4f399f482 Mon Sep 17 00:00:00 2001 From: Kimberly Date: Mon, 8 May 2023 22:58:03 +0000 Subject: [PATCH 17/36] Translated using Weblate (Spanish) Currently translated at 94.2% (1138 of 1208 strings) Co-authored-by: Kimberly Translate-URL: http://translate.akkoma.dev/projects/foundkey/foundkey/es/ Translation: Foundkey/foundkey --- locales/es-ES.yml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/locales/es-ES.yml b/locales/es-ES.yml index 803f90418..fbfcd9352 100644 --- a/locales/es-ES.yml +++ b/locales/es-ES.yml @@ -854,6 +854,18 @@ _mfm: spin: Animación (Spin) shakeDescription: Brinda al contenido una animación temblorosa. inlineMath: Función matemática (Inline) + rainbow: Arcoíris + x4Description: Muestra el contenido de la manera más grandemente posible. + blurDescription: Muestra borroso el contenido. Se mostrará con claridad cuando se + cubra. + spinDescription: Da al contenido una animación de girar. + x2: Grande + x2Description: Muestra en grande el contenido. + x3Description: Muestra más grande el contenido. + x4: Increíblemente grande + blur: Borroso + fontDescription: Agrega la fuente para mostrar contenido. + x3: Muy grande _instanceTicker: none: "No mostrar" remote: "Mostrar a usuarios remotos" @@ -1318,3 +1330,9 @@ unlikeConfirm: ¿En verdad quieres remover tu like? breakFollow: Quitar seguidor reporter: Reportero continueThread: Ver la continuación del hilo +uploadFailedSize: El archivo es muy grande para subirse. +uploadFailed: Subida fallida +uploadFailedDescription: No se pudo subir el archivo. +movedTo: Este usuario se ha movido a {handle}. +attachedToNotes: Notas del archivo +showAttachedNotes: Mostrar notas del archivo From c53486a47c2f0e48b2a5e2ca7b4f1d5f18396411 Mon Sep 17 00:00:00 2001 From: Johann150 Date: Mon, 15 May 2023 20:14:51 +0200 Subject: [PATCH 18/36] try to fix tests --- packages/backend/test/api.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/backend/test/api.ts b/packages/backend/test/api.ts index ae46ae92d..222c8d4d4 100644 --- a/packages/backend/test/api.ts +++ b/packages/backend/test/api.ts @@ -10,7 +10,7 @@ describe('API', () => { let bob: any; let carol: any; - before(async () => { + before(async function() { this.timeout(0); p = await startServer(); alice = await signup({ username: 'alice' }); From 9675ced91551c9986aa198097a1aca5851c19a25 Mon Sep 17 00:00:00 2001 From: Johann150 Date: Thu, 18 May 2023 02:08:07 +0200 Subject: [PATCH 19/36] translate japanese comment --- packages/backend/src/remote/activitypub/models/person.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/backend/src/remote/activitypub/models/person.ts b/packages/backend/src/remote/activitypub/models/person.ts index 17d9858e4..80993a333 100644 --- a/packages/backend/src/remote/activitypub/models/person.ts +++ b/packages/backend/src/remote/activitypub/models/person.ts @@ -217,7 +217,7 @@ export async function createPerson(value: string | IObject, resolver: Resolver): } catch (e) { // duplicate key error if (isDuplicateKeyValueError(e)) { - // /users/@a => /users/:id のように入力がaliasなときにエラーになることがあるのを対応 + // Fix an error when the input is an alias like /users/@a -> /users/:id const u = await Users.findOneBy({ uri: person.id, }); From 1516ddfc9bebe946cb94f8cbcabfc586db098823 Mon Sep 17 00:00:00 2001 From: Johann150 Date: Thu, 18 May 2023 13:25:57 +0200 Subject: [PATCH 20/36] refactor: remove CacheableUser & co The CacheableUser, CacheableLocalUser and CacheableRemoteUser are identical types to User, ILocalUser and IRemoteUser so it seems nonsensical to have different types for them. --- packages/backend/src/models/entities/user.ts | 6 ------ .../backend/src/remote/activitypub/audience.ts | 16 ++++++++-------- .../src/remote/activitypub/db-resolver.ts | 4 ++-- .../remote/activitypub/kernel/accept/follow.ts | 6 +++--- .../remote/activitypub/kernel/accept/index.ts | 4 ++-- .../src/remote/activitypub/kernel/add/index.ts | 4 ++-- .../remote/activitypub/kernel/announce/index.ts | 4 ++-- .../remote/activitypub/kernel/announce/note.ts | 4 ++-- .../src/remote/activitypub/kernel/block/index.ts | 8 ++++---- .../remote/activitypub/kernel/create/index.ts | 4 ++-- .../src/remote/activitypub/kernel/create/note.ts | 4 ++-- .../remote/activitypub/kernel/delete/actor.ts | 4 ++-- .../remote/activitypub/kernel/delete/index.ts | 4 ++-- .../src/remote/activitypub/kernel/delete/note.ts | 4 ++-- .../src/remote/activitypub/kernel/flag/index.ts | 9 +++++---- .../src/remote/activitypub/kernel/follow.ts | 4 ++-- .../src/remote/activitypub/kernel/index.ts | 6 +++--- .../src/remote/activitypub/kernel/like.ts | 4 ++-- .../src/remote/activitypub/kernel/move/index.ts | 4 ++-- .../src/remote/activitypub/kernel/read.ts | 4 ++-- .../remote/activitypub/kernel/reject/follow.ts | 6 +++--- .../remote/activitypub/kernel/reject/index.ts | 4 ++-- .../remote/activitypub/kernel/remove/index.ts | 4 ++-- .../src/remote/activitypub/kernel/undo/accept.ts | 4 ++-- .../remote/activitypub/kernel/undo/announce.ts | 4 ++-- .../src/remote/activitypub/kernel/undo/block.ts | 4 ++-- .../src/remote/activitypub/kernel/undo/follow.ts | 4 ++-- .../src/remote/activitypub/kernel/undo/index.ts | 4 ++-- .../src/remote/activitypub/kernel/undo/like.ts | 4 ++-- .../remote/activitypub/kernel/update/index.ts | 4 ++-- .../src/remote/activitypub/misc/auth-user.ts | 4 ++-- .../src/remote/activitypub/models/image.ts | 6 +++--- .../src/remote/activitypub/models/mention.ts | 8 ++++---- .../src/remote/activitypub/models/note.ts | 4 ++-- .../src/remote/activitypub/models/person.ts | 6 +++--- .../backend/src/remote/activitypub/perform.ts | 4 ++-- packages/backend/src/server/api/authenticate.ts | 4 ++-- packages/backend/src/server/api/call.ts | 4 ++-- packages/backend/src/server/api/define.ts | 8 ++++---- .../backend/src/server/api/endpoints/ap/show.ts | 6 +++--- packages/backend/src/services/blocking/delete.ts | 4 ++-- packages/backend/src/services/messages/create.ts | 4 ++-- packages/backend/src/services/note/polls/vote.ts | 4 ++-- packages/backend/src/services/user-cache.ts | 4 ++-- 44 files changed, 107 insertions(+), 112 deletions(-) diff --git a/packages/backend/src/models/entities/user.ts b/packages/backend/src/models/entities/user.ts index 6cfca187a..0c1e8092f 100644 --- a/packages/backend/src/models/entities/user.ts +++ b/packages/backend/src/models/entities/user.ts @@ -260,9 +260,3 @@ export interface IRemoteUser extends User { host: string; token: null; } - -export type CacheableLocalUser = ILocalUser; - -export type CacheableRemoteUser = IRemoteUser; - -export type CacheableUser = CacheableLocalUser | CacheableRemoteUser; diff --git a/packages/backend/src/remote/activitypub/audience.ts b/packages/backend/src/remote/activitypub/audience.ts index 9c04ecb6d..9125660f3 100644 --- a/packages/backend/src/remote/activitypub/audience.ts +++ b/packages/backend/src/remote/activitypub/audience.ts @@ -1,5 +1,5 @@ import promiseLimit from 'promise-limit'; -import { CacheableRemoteUser, CacheableUser } from '@/models/entities/user.js'; +import { IRemoteUser, User } from '@/models/entities/user.js'; import { unique, concat } from '@/prelude/array.js'; import { resolvePerson } from './models/person.js'; import { Resolver } from './resolver.js'; @@ -9,20 +9,20 @@ type Visibility = 'public' | 'home' | 'followers' | 'specified'; type AudienceInfo = { visibility: Visibility, - mentionedUsers: CacheableUser[], - visibleUsers: CacheableUser[], + mentionedUsers: User[], + visibleUsers: User[], }; -export async function parseAudience(actor: CacheableRemoteUser, to?: ApObject, cc?: ApObject, resolver?: Resolver): Promise { +export async function parseAudience(actor: IRemoteUser, to?: ApObject, cc?: ApObject, resolver?: Resolver): Promise { const toGroups = groupingAudience(getApIds(to), actor); const ccGroups = groupingAudience(getApIds(cc), actor); const others = unique(concat([toGroups.other, ccGroups.other])); - const limit = promiseLimit(2); + const limit = promiseLimit(2); const mentionedUsers = (await Promise.all( others.map(id => limit(() => resolvePerson(id, resolver).catch(() => null))), - )).filter((x): x is CacheableUser => x != null); + )).filter((x): x is User => x != null); if (toGroups.public.length > 0) { return { @@ -55,7 +55,7 @@ export async function parseAudience(actor: CacheableRemoteUser, to?: ApObject, c }; } -function groupingAudience(ids: string[], actor: CacheableRemoteUser) { +function groupingAudience(ids: string[], actor: IRemoteUser) { const groups = { public: [] as string[], followers: [] as string[], @@ -85,7 +85,7 @@ function isPublic(id: string) { ].includes(id); } -function isFollowers(id: string, actor: CacheableRemoteUser) { +function isFollowers(id: string, actor: IRemoteUser) { return ( id === (actor.followersUri || `${actor.uri}/followers`) ); diff --git a/packages/backend/src/remote/activitypub/db-resolver.ts b/packages/backend/src/remote/activitypub/db-resolver.ts index dc1b47fef..4684b8931 100644 --- a/packages/backend/src/remote/activitypub/db-resolver.ts +++ b/packages/backend/src/remote/activitypub/db-resolver.ts @@ -1,7 +1,7 @@ import escapeRegexp from 'escape-regexp'; import config from '@/config/index.js'; import { Note } from '@/models/entities/note.js'; -import { CacheableUser } from '@/models/entities/user.js'; +import { User } from '@/models/entities/user.js'; import { MessagingMessage } from '@/models/entities/messaging-message.js'; import { Notes, MessagingMessages } from '@/models/index.js'; import { uriPersonCache, userByIdCache } from '@/services/user-cache.js'; @@ -89,7 +89,7 @@ export class DbResolver { /** * AP Person => FoundKey User in DB */ - public async getUserFromApId(value: string | IObject): Promise { + public async getUserFromApId(value: string | IObject): Promise { const parsed = parseUri(value); if (parsed.local) { diff --git a/packages/backend/src/remote/activitypub/kernel/accept/follow.ts b/packages/backend/src/remote/activitypub/kernel/accept/follow.ts index 037f660c6..05c621e6d 100644 --- a/packages/backend/src/remote/activitypub/kernel/accept/follow.ts +++ b/packages/backend/src/remote/activitypub/kernel/accept/follow.ts @@ -1,11 +1,11 @@ -import { CacheableRemoteUser } from '@/models/entities/user.js'; +import { IRemoteUser } from '@/models/entities/user.js'; import { acceptFollowRequest } from '@/services/following/requests/accept.js'; import { relayAccepted } from '@/services/relay.js'; import { IFollow } from '@/remote/activitypub/type.js'; import { DbResolver } from '@/remote/activitypub/db-resolver.js'; -export default async (actor: CacheableRemoteUser, activity: IFollow): Promise => { - // ※ activityはこっちから投げたフォローリクエストなので、activity.actorは存在するローカルユーザーである必要がある +export default async (actor: IRemoteUser, activity: IFollow): Promise => { + // activity is a follow request started by this server, so activity.actor must be an existing local user. const dbResolver = new DbResolver(); const follower = await dbResolver.getUserFromApId(activity.actor); diff --git a/packages/backend/src/remote/activitypub/kernel/accept/index.ts b/packages/backend/src/remote/activitypub/kernel/accept/index.ts index be9b80096..1a61011e6 100644 --- a/packages/backend/src/remote/activitypub/kernel/accept/index.ts +++ b/packages/backend/src/remote/activitypub/kernel/accept/index.ts @@ -1,10 +1,10 @@ -import { CacheableRemoteUser } from '@/models/entities/user.js'; +import { IRemoteUser } from '@/models/entities/user.js'; import { apLogger } from '@/remote/activitypub/logger.js'; import { Resolver } from '@/remote/activitypub/resolver.js'; import { IAccept, isFollow, getApType } from '@/remote/activitypub/type.js'; import acceptFollow from './follow.js'; -export default async (actor: CacheableRemoteUser, activity: IAccept, resolver: Resolver): Promise => { +export default async (actor: IRemoteUser, activity: IAccept, resolver: Resolver): Promise => { const uri = activity.id || activity; apLogger.info(`Accept: ${uri}`); diff --git a/packages/backend/src/remote/activitypub/kernel/add/index.ts b/packages/backend/src/remote/activitypub/kernel/add/index.ts index 3fd5f4723..3d685dae4 100644 --- a/packages/backend/src/remote/activitypub/kernel/add/index.ts +++ b/packages/backend/src/remote/activitypub/kernel/add/index.ts @@ -1,10 +1,10 @@ -import { CacheableRemoteUser } from '@/models/entities/user.js'; +import { IRemoteUser } from '@/models/entities/user.js'; import { addPinned } from '@/services/i/pin.js'; import { resolveNote } from '@/remote/activitypub/models/note.js'; import { IAdd } from '@/remote/activitypub/type.js'; import { Resolver } from '@/remote/activitypub/resolver.js'; -export default async (actor: CacheableRemoteUser, activity: IAdd, resolver: Resolver): Promise => { +export default async (actor: IRemoteUser, activity: IAdd, resolver: Resolver): Promise => { if ('actor' in activity && actor.uri !== activity.actor) { throw new Error('invalid actor'); } diff --git a/packages/backend/src/remote/activitypub/kernel/announce/index.ts b/packages/backend/src/remote/activitypub/kernel/announce/index.ts index e4d77e1c5..25bc8c73c 100644 --- a/packages/backend/src/remote/activitypub/kernel/announce/index.ts +++ b/packages/backend/src/remote/activitypub/kernel/announce/index.ts @@ -1,10 +1,10 @@ -import { CacheableRemoteUser } from '@/models/entities/user.js'; +import { IRemoteUser } from '@/models/entities/user.js'; import { apLogger } from '@/remote/activitypub/logger.js'; import { Resolver } from '@/remote/activitypub/resolver.js'; import { IAnnounce, getApId } from '@/remote/activitypub/type.js'; import announceNote from './note.js'; -export default async (actor: CacheableRemoteUser, activity: IAnnounce, resolver: Resolver): Promise => { +export default async (actor: IRemoteUser, activity: IAnnounce, resolver: Resolver): Promise => { const uri = getApId(activity); apLogger.info(`Announce: ${uri}`); diff --git a/packages/backend/src/remote/activitypub/kernel/announce/note.ts b/packages/backend/src/remote/activitypub/kernel/announce/note.ts index 254ef2727..5c6196588 100644 --- a/packages/backend/src/remote/activitypub/kernel/announce/note.ts +++ b/packages/backend/src/remote/activitypub/kernel/announce/note.ts @@ -1,5 +1,5 @@ import post from '@/services/note/create.js'; -import { CacheableRemoteUser } from '@/models/entities/user.js'; +import { IRemoteUser } from '@/models/entities/user.js'; import { extractDbHost } from '@/misc/convert-host.js'; import { getApLock } from '@/misc/app-lock.js'; import { StatusError } from '@/misc/fetch.js'; @@ -11,7 +11,7 @@ import { Resolver } from '@/remote/activitypub/resolver.js'; import { IAnnounce, getApId } from '@/remote/activitypub/type.js'; import { shouldBlockInstance } from '@/misc/should-block-instance.js'; -export default async function(resolver: Resolver, actor: CacheableRemoteUser, activity: IAnnounce, targetUri: string): Promise { +export default async function(resolver: Resolver, actor: IRemoteUser, activity: IAnnounce, targetUri: string): Promise { const uri = getApId(activity); if (actor.isSuspended) { diff --git a/packages/backend/src/remote/activitypub/kernel/block/index.ts b/packages/backend/src/remote/activitypub/kernel/block/index.ts index 7095a36a5..08b7ee517 100644 --- a/packages/backend/src/remote/activitypub/kernel/block/index.ts +++ b/packages/backend/src/remote/activitypub/kernel/block/index.ts @@ -1,11 +1,11 @@ import block from '@/services/blocking/create.js'; -import { CacheableRemoteUser } from '@/models/entities/user.js'; +import { IRemoteUser } from '@/models/entities/user.js'; import { Users } from '@/models/index.js'; import { DbResolver } from '@/remote/activitypub/db-resolver.js'; import { IBlock } from '@/remote/activitypub/type.js'; -export default async (actor: CacheableRemoteUser, activity: IBlock): Promise => { - // ※ activity.objectにブロック対象があり、それは存在するローカルユーザーのはず +export default async (actor: IRemoteUser, activity: IBlock): Promise => { + // There is a block target in activity.object, which should be a local user that exists. const dbResolver = new DbResolver(); const blockee = await dbResolver.getUserFromApId(activity.object); @@ -15,7 +15,7 @@ export default async (actor: CacheableRemoteUser, activity: IBlock): Promise => { +export default async (actor: IRemoteUser, activity: ICreate, resolver: Resolver): Promise => { const uri = getApId(activity); apLogger.info(`Create: ${uri}`); diff --git a/packages/backend/src/remote/activitypub/kernel/create/note.ts b/packages/backend/src/remote/activitypub/kernel/create/note.ts index 892dbb26a..6fc7b6c2d 100644 --- a/packages/backend/src/remote/activitypub/kernel/create/note.ts +++ b/packages/backend/src/remote/activitypub/kernel/create/note.ts @@ -1,4 +1,4 @@ -import { CacheableRemoteUser } from '@/models/entities/user.js'; +import { IRemoteUser } from '@/models/entities/user.js'; import { getApLock } from '@/misc/app-lock.js'; import { extractDbHost } from '@/misc/convert-host.js'; import { StatusError } from '@/misc/fetch.js'; @@ -9,7 +9,7 @@ import { getApId, IObject } from '@/remote/activitypub/type.js'; /** * 投稿作成アクティビティを捌きます */ -export default async function(resolver: Resolver, actor: CacheableRemoteUser, note: IObject, silent = false): Promise { +export default async function(resolver: Resolver, actor: IRemoteUser, note: IObject, silent = false): Promise { const uri = getApId(note); if (typeof note === 'object') { diff --git a/packages/backend/src/remote/activitypub/kernel/delete/actor.ts b/packages/backend/src/remote/activitypub/kernel/delete/actor.ts index ea75a9739..9513ea22f 100644 --- a/packages/backend/src/remote/activitypub/kernel/delete/actor.ts +++ b/packages/backend/src/remote/activitypub/kernel/delete/actor.ts @@ -1,9 +1,9 @@ -import { CacheableRemoteUser } from '@/models/entities/user.js'; +import { IRemoteUser } from '@/models/entities/user.js'; import { Users } from '@/models/index.js'; import { apLogger } from '@/remote/activitypub/logger.js'; import { deleteAccount } from '@/services/delete-account.js'; -export async function deleteActor(actor: CacheableRemoteUser, uri: string): Promise { +export async function deleteActor(actor: IRemoteUser, uri: string): Promise { apLogger.info(`Deleting the Actor: ${uri}`); if (actor.uri !== uri) { diff --git a/packages/backend/src/remote/activitypub/kernel/delete/index.ts b/packages/backend/src/remote/activitypub/kernel/delete/index.ts index ee05e5327..9e05c9e48 100644 --- a/packages/backend/src/remote/activitypub/kernel/delete/index.ts +++ b/packages/backend/src/remote/activitypub/kernel/delete/index.ts @@ -1,4 +1,4 @@ -import { CacheableRemoteUser } from '@/models/entities/user.js'; +import { IRemoteUser } from '@/models/entities/user.js'; import { toSingle } from '@/prelude/array.js'; import { IDelete, getApId, isTombstone, IObject, validPost, validActor } from '@/remote/activitypub/type.js'; import { deleteActor } from './actor.js'; @@ -7,7 +7,7 @@ import deleteNote from './note.js'; /** * 削除アクティビティを捌きます */ -export default async (actor: CacheableRemoteUser, activity: IDelete): Promise => { +export default async (actor: IRemoteUser, activity: IDelete): Promise => { if ('actor' in activity && actor.uri !== activity.actor) { throw new Error('invalid actor'); } diff --git a/packages/backend/src/remote/activitypub/kernel/delete/note.ts b/packages/backend/src/remote/activitypub/kernel/delete/note.ts index 9f9a5cea6..d855f7f92 100644 --- a/packages/backend/src/remote/activitypub/kernel/delete/note.ts +++ b/packages/backend/src/remote/activitypub/kernel/delete/note.ts @@ -1,11 +1,11 @@ -import { CacheableRemoteUser } from '@/models/entities/user.js'; +import { IRemoteUser } from '@/models/entities/user.js'; import { deleteNotes } from '@/services/note/delete.js'; import { getApLock } from '@/misc/app-lock.js'; import { deleteMessage } from '@/services/messages/delete.js'; import { DbResolver } from '@/remote/activitypub/db-resolver.js'; import { apLogger } from '@/remote/activitypub/logger.js'; -export default async function(actor: CacheableRemoteUser, uri: string): Promise { +export default async function(actor: IRemoteUser, uri: string): Promise { apLogger.info(`Deleting the Note: ${uri}`); const unlock = await getApLock(uri); diff --git a/packages/backend/src/remote/activitypub/kernel/flag/index.ts b/packages/backend/src/remote/activitypub/kernel/flag/index.ts index e50bcc2bd..cadb7436d 100644 --- a/packages/backend/src/remote/activitypub/kernel/flag/index.ts +++ b/packages/backend/src/remote/activitypub/kernel/flag/index.ts @@ -1,13 +1,14 @@ import { In } from 'typeorm'; import config from '@/config/index.js'; import { genId } from '@/misc/gen-id.js'; -import { CacheableRemoteUser } from '@/models/entities/user.js'; +import { IRemoteUser } from '@/models/entities/user.js'; import { AbuseUserReports, Users } from '@/models/index.js'; import { IFlag, getApIds } from '@/remote/activitypub/type.js'; -export default async (actor: CacheableRemoteUser, activity: IFlag): Promise => { - // objectは `(User|Note) | (User|Note)[]` だけど、全パターンDBスキーマと対応させられないので - // 対象ユーザーは一番最初のユーザー として あとはコメントとして格納する +export default async (actor: IRemoteUser, activity: IFlag): Promise => { + // The object is `(User|Note) | (User|Note)[]`, but since the database schema + // cannot be made to handle every possible case, the target user is the first user + // and everything else is stored by URL. const uris = getApIds(activity.object); const userIds = uris.filter(uri => uri.startsWith(config.url + '/users/')).map(uri => uri.split('/').pop()!); diff --git a/packages/backend/src/remote/activitypub/kernel/follow.ts b/packages/backend/src/remote/activitypub/kernel/follow.ts index 8125b4606..99dbb369c 100644 --- a/packages/backend/src/remote/activitypub/kernel/follow.ts +++ b/packages/backend/src/remote/activitypub/kernel/follow.ts @@ -1,9 +1,9 @@ -import { CacheableRemoteUser } from '@/models/entities/user.js'; +import { IRemoteUser } from '@/models/entities/user.js'; import follow from '@/services/following/create.js'; import { IFollow } from '../type.js'; import { DbResolver } from '../db-resolver.js'; -export default async (actor: CacheableRemoteUser, activity: IFollow): Promise => { +export default async (actor: IRemoteUser, activity: IFollow): Promise => { const dbResolver = new DbResolver(); const followee = await dbResolver.getUserFromApId(activity.object); diff --git a/packages/backend/src/remote/activitypub/kernel/index.ts b/packages/backend/src/remote/activitypub/kernel/index.ts index 46a972a7e..2a0918a4d 100644 --- a/packages/backend/src/remote/activitypub/kernel/index.ts +++ b/packages/backend/src/remote/activitypub/kernel/index.ts @@ -1,4 +1,4 @@ -import { CacheableRemoteUser } from '@/models/entities/user.js'; +import { IRemoteUser } from '@/models/entities/user.js'; import { toArray } from '@/prelude/array.js'; import { Resolver } from '@/remote/activitypub/resolver.js'; import { extractDbHost } from '@/misc/convert-host.js'; @@ -21,7 +21,7 @@ import block from './block/index.js'; import flag from './flag/index.js'; import { move } from './move/index.js'; -export async function performActivity(actor: CacheableRemoteUser, activity: IObject, resolver: Resolver): Promise { +export async function performActivity(actor: IRemoteUser, activity: IObject, resolver: Resolver): Promise { if (isCollectionOrOrderedCollection(activity)) { for (const item of toArray(isCollection(activity) ? activity.items : activity.orderedItems)) { const act = await resolver.resolve(item); @@ -38,7 +38,7 @@ export async function performActivity(actor: CacheableRemoteUser, activity: IObj } } -async function performOneActivity(actor: CacheableRemoteUser, activity: IObject, resolver: Resolver): Promise { +async function performOneActivity(actor: IRemoteUser, activity: IObject, resolver: Resolver): Promise { if (actor.isSuspended) return; if (typeof activity.id !== 'undefined') { diff --git a/packages/backend/src/remote/activitypub/kernel/like.ts b/packages/backend/src/remote/activitypub/kernel/like.ts index 9650312b3..51270cc80 100644 --- a/packages/backend/src/remote/activitypub/kernel/like.ts +++ b/packages/backend/src/remote/activitypub/kernel/like.ts @@ -1,9 +1,9 @@ -import { CacheableRemoteUser } from '@/models/entities/user.js'; +import { IRemoteUser } from '@/models/entities/user.js'; import { createReaction } from '@/services/note/reaction/create.js'; import { ILike, getApId } from '../type.js'; import { fetchNote, extractEmojis } from '../models/note.js'; -export default async (actor: CacheableRemoteUser, activity: ILike) => { +export default async (actor: IRemoteUser, activity: ILike) => { const targetUri = getApId(activity.object); const note = await fetchNote(targetUri); diff --git a/packages/backend/src/remote/activitypub/kernel/move/index.ts b/packages/backend/src/remote/activitypub/kernel/move/index.ts index e64656e09..8f233d869 100644 --- a/packages/backend/src/remote/activitypub/kernel/move/index.ts +++ b/packages/backend/src/remote/activitypub/kernel/move/index.ts @@ -1,12 +1,12 @@ import { IsNull } from 'typeorm'; -import { CacheableRemoteUser } from '@/models/entities/user.js'; +import { IRemoteUser } from '@/models/entities/user.js'; import { resolvePerson } from '@/remote/activitypub/models/person.js'; import { Followings, Users } from '@/models/index.js'; import { createNotification } from '@/services/create-notification.js'; import Resolver from '../../resolver.js'; import { IMove, isActor, getApId } from '../../type.js'; -export async function move(actor: CacheableRemoteUser, activity: IMove, resolver: Resolver): Promise { +export async function move(actor: IRemoteUser, activity: IMove, resolver: Resolver): Promise { // actor is not move origin if (activity.object == null || getApId(activity.object) !== actor.uri) return; diff --git a/packages/backend/src/remote/activitypub/kernel/read.ts b/packages/backend/src/remote/activitypub/kernel/read.ts index d367fb669..cb147f2af 100644 --- a/packages/backend/src/remote/activitypub/kernel/read.ts +++ b/packages/backend/src/remote/activitypub/kernel/read.ts @@ -1,10 +1,10 @@ -import { CacheableRemoteUser } from '@/models/entities/user.js'; +import { IRemoteUser } from '@/models/entities/user.js'; import { isSelfHost, extractDbHost } from '@/misc/convert-host.js'; import { MessagingMessages } from '@/models/index.js'; import { readUserMessagingMessage } from '@/server/api/common/read-messaging-message.js'; import { IRead, getApId } from '../type.js'; -export const performReadActivity = async (actor: CacheableRemoteUser, activity: IRead): Promise => { +export const performReadActivity = async (actor: IRemoteUser, activity: IRead): Promise => { const id = await getApId(activity.object); if (!isSelfHost(extractDbHost(id))) { diff --git a/packages/backend/src/remote/activitypub/kernel/reject/follow.ts b/packages/backend/src/remote/activitypub/kernel/reject/follow.ts index bd3ad1660..2606b8a5e 100644 --- a/packages/backend/src/remote/activitypub/kernel/reject/follow.ts +++ b/packages/backend/src/remote/activitypub/kernel/reject/follow.ts @@ -1,12 +1,12 @@ -import { CacheableRemoteUser } from '@/models/entities/user.js'; +import { IRemoteUser } from '@/models/entities/user.js'; import { remoteReject } from '@/services/following/reject.js'; import { relayRejected } from '@/services/relay.js'; import { Users } from '@/models/index.js'; import { IFollow } from '../../type.js'; import { DbResolver } from '../../db-resolver.js'; -export default async (actor: CacheableRemoteUser, activity: IFollow): Promise => { - // ※ activityはこっちから投げたフォローリクエストなので、activity.actorは存在するローカルユーザーである必要がある +export default async (actor: IRemoteUser, activity: IFollow): Promise => { + // activity is a follow request started by this server, so activity.actor must be an existing local user. const dbResolver = new DbResolver(); const follower = await dbResolver.getUserFromApId(activity.actor); diff --git a/packages/backend/src/remote/activitypub/kernel/reject/index.ts b/packages/backend/src/remote/activitypub/kernel/reject/index.ts index 3a91c8ec7..3eb748f6b 100644 --- a/packages/backend/src/remote/activitypub/kernel/reject/index.ts +++ b/packages/backend/src/remote/activitypub/kernel/reject/index.ts @@ -1,10 +1,10 @@ -import { CacheableRemoteUser } from '@/models/entities/user.js'; +import { IRemoteUser } from '@/models/entities/user.js'; import { Resolver } from '@/remote/activitypub/resolver.js'; import { apLogger } from '../../logger.js'; import { IReject, isFollow, getApType } from '../../type.js'; import rejectFollow from './follow.js'; -export default async (actor: CacheableRemoteUser, activity: IReject, resolver: Resolver): Promise => { +export default async (actor: IRemoteUser, activity: IReject, resolver: Resolver): Promise => { const uri = activity.id || activity; apLogger.info(`Reject: ${uri}`); diff --git a/packages/backend/src/remote/activitypub/kernel/remove/index.ts b/packages/backend/src/remote/activitypub/kernel/remove/index.ts index 6591f82b1..3f7ad494e 100644 --- a/packages/backend/src/remote/activitypub/kernel/remove/index.ts +++ b/packages/backend/src/remote/activitypub/kernel/remove/index.ts @@ -1,10 +1,10 @@ -import { CacheableRemoteUser } from '@/models/entities/user.js'; +import { IRemoteUser } from '@/models/entities/user.js'; import { removePinned } from '@/services/i/pin.js'; import { Resolver } from '@/remote/activitypub/resolver.js'; import { IRemove } from '../../type.js'; import { resolveNote } from '../../models/note.js'; -export default async (actor: CacheableRemoteUser, activity: IRemove, resolver: Resolver): Promise => { +export default async (actor: IRemoteUser, activity: IRemove, resolver: Resolver): Promise => { if ('actor' in activity && actor.uri !== activity.actor) { throw new Error('invalid actor'); } diff --git a/packages/backend/src/remote/activitypub/kernel/undo/accept.ts b/packages/backend/src/remote/activitypub/kernel/undo/accept.ts index fa4eea44c..b14cec889 100644 --- a/packages/backend/src/remote/activitypub/kernel/undo/accept.ts +++ b/packages/backend/src/remote/activitypub/kernel/undo/accept.ts @@ -1,10 +1,10 @@ import unfollow from '@/services/following/delete.js'; -import { CacheableRemoteUser } from '@/models/entities/user.js'; +import { IRemoteUser } from '@/models/entities/user.js'; import { Followings } from '@/models/index.js'; import { DbResolver } from '@/remote/activitypub/db-resolver.js'; import { IAccept } from '@/remote/activitypub/type.js'; -export default async (actor: CacheableRemoteUser, activity: IAccept): Promise => { +export default async (actor: IRemoteUser, activity: IAccept): Promise => { const dbResolver = new DbResolver(); const follower = await dbResolver.getUserFromApId(activity.object); diff --git a/packages/backend/src/remote/activitypub/kernel/undo/announce.ts b/packages/backend/src/remote/activitypub/kernel/undo/announce.ts index 78981b542..05e0edbb6 100644 --- a/packages/backend/src/remote/activitypub/kernel/undo/announce.ts +++ b/packages/backend/src/remote/activitypub/kernel/undo/announce.ts @@ -1,9 +1,9 @@ import { Notes } from '@/models/index.js'; -import { CacheableRemoteUser } from '@/models/entities/user.js'; +import { IRemoteUser } from '@/models/entities/user.js'; import { deleteNotes } from '@/services/note/delete.js'; import { IAnnounce, getApId } from '@/remote/activitypub/type.js'; -export const undoAnnounce = async (actor: CacheableRemoteUser, activity: IAnnounce): Promise => { +export const undoAnnounce = async (actor: IRemoteUser, activity: IAnnounce): Promise => { const uri = getApId(activity); const note = await Notes.findOneBy({ diff --git a/packages/backend/src/remote/activitypub/kernel/undo/block.ts b/packages/backend/src/remote/activitypub/kernel/undo/block.ts index ae1c9c0b6..f4e0513fb 100644 --- a/packages/backend/src/remote/activitypub/kernel/undo/block.ts +++ b/packages/backend/src/remote/activitypub/kernel/undo/block.ts @@ -1,10 +1,10 @@ import unblock from '@/services/blocking/delete.js'; -import { CacheableRemoteUser } from '@/models/entities/user.js'; +import { IRemoteUser } from '@/models/entities/user.js'; import { Users } from '@/models/index.js'; import { IBlock } from '@/remote/activitypub/type.js'; import { DbResolver } from '@/remote/activitypub/db-resolver.js'; -export default async (actor: CacheableRemoteUser, activity: IBlock): Promise => { +export default async (actor: IRemoteUser, activity: IBlock): Promise => { const dbResolver = new DbResolver(); const blockee = await dbResolver.getUserFromApId(activity.object); diff --git a/packages/backend/src/remote/activitypub/kernel/undo/follow.ts b/packages/backend/src/remote/activitypub/kernel/undo/follow.ts index c7f99bcf2..172ee8460 100644 --- a/packages/backend/src/remote/activitypub/kernel/undo/follow.ts +++ b/packages/backend/src/remote/activitypub/kernel/undo/follow.ts @@ -1,11 +1,11 @@ import unfollow from '@/services/following/delete.js'; import { cancelFollowRequest } from '@/services/following/requests/cancel.js'; -import { CacheableRemoteUser } from '@/models/entities/user.js'; +import { IRemoteUser } from '@/models/entities/user.js'; import { FollowRequests, Followings } from '@/models/index.js'; import { IFollow } from '@/remote/activitypub/type.js'; import { DbResolver } from '@/remote/activitypub/db-resolver.js'; -export default async (actor: CacheableRemoteUser, activity: IFollow): Promise => { +export default async (actor: IRemoteUser, activity: IFollow): Promise => { const dbResolver = new DbResolver(); const followee = await dbResolver.getUserFromApId(activity.object); diff --git a/packages/backend/src/remote/activitypub/kernel/undo/index.ts b/packages/backend/src/remote/activitypub/kernel/undo/index.ts index 05382f0f5..139711129 100644 --- a/packages/backend/src/remote/activitypub/kernel/undo/index.ts +++ b/packages/backend/src/remote/activitypub/kernel/undo/index.ts @@ -1,4 +1,4 @@ -import { CacheableRemoteUser } from '@/models/entities/user.js'; +import { IRemoteUser } from '@/models/entities/user.js'; import { apLogger } from '@/remote/activitypub/logger.js'; import { Resolver } from '@/remote/activitypub/resolver.js'; import { IUndo, isFollow, isBlock, isLike, isAnnounce, getApType, isAccept } from '@/remote/activitypub/type.js'; @@ -8,7 +8,7 @@ import undoLike from './like.js'; import undoAccept from './accept.js'; import { undoAnnounce } from './announce.js'; -export default async (actor: CacheableRemoteUser, activity: IUndo, resolver: Resolver): Promise => { +export default async (actor: IRemoteUser, activity: IUndo, resolver: Resolver): Promise => { if ('actor' in activity && actor.uri !== activity.actor) { throw new Error('invalid actor'); } diff --git a/packages/backend/src/remote/activitypub/kernel/undo/like.ts b/packages/backend/src/remote/activitypub/kernel/undo/like.ts index 6c7b8d18b..717c8aa2a 100644 --- a/packages/backend/src/remote/activitypub/kernel/undo/like.ts +++ b/packages/backend/src/remote/activitypub/kernel/undo/like.ts @@ -1,4 +1,4 @@ -import { CacheableRemoteUser } from '@/models/entities/user.js'; +import { IRemoteUser } from '@/models/entities/user.js'; import { deleteReaction } from '@/services/note/reaction/delete.js'; import { ILike, getApId } from '@/remote/activitypub/type.js'; import { fetchNote } from '@/remote/activitypub/models/note.js'; @@ -6,7 +6,7 @@ import { fetchNote } from '@/remote/activitypub/models/note.js'; /** * Process Undo.Like activity */ -export default async (actor: CacheableRemoteUser, activity: ILike) => { +export default async (actor: IRemoteUser, activity: ILike) => { const targetUri = getApId(activity.object); const note = await fetchNote(targetUri); diff --git a/packages/backend/src/remote/activitypub/kernel/update/index.ts b/packages/backend/src/remote/activitypub/kernel/update/index.ts index 73085b181..d34965db2 100644 --- a/packages/backend/src/remote/activitypub/kernel/update/index.ts +++ b/packages/backend/src/remote/activitypub/kernel/update/index.ts @@ -1,4 +1,4 @@ -import { CacheableRemoteUser } from '@/models/entities/user.js'; +import { IRemoteUser } from '@/models/entities/user.js'; import { getApId, getApType, IUpdate, isActor } from '@/remote/activitypub/type.js'; import { apLogger } from '@/remote/activitypub/logger.js'; import { updateQuestion } from '@/remote/activitypub/models/question.js'; @@ -8,7 +8,7 @@ import { updatePerson } from '@/remote/activitypub/models/person.js'; /** * Updateアクティビティを捌きます */ -export default async (actor: CacheableRemoteUser, activity: IUpdate, resolver: Resolver): Promise => { +export default async (actor: IRemoteUser, activity: IUpdate, resolver: Resolver): Promise => { if ('actor' in activity && actor.uri !== activity.actor) { return 'skip: invalid actor'; } diff --git a/packages/backend/src/remote/activitypub/misc/auth-user.ts b/packages/backend/src/remote/activitypub/misc/auth-user.ts index 4705bb791..e140d2fa1 100644 --- a/packages/backend/src/remote/activitypub/misc/auth-user.ts +++ b/packages/backend/src/remote/activitypub/misc/auth-user.ts @@ -1,12 +1,12 @@ import { Cache } from '@/misc/cache.js'; import { UserPublickeys } from '@/models/index.js'; -import { CacheableRemoteUser } from '@/models/entities/user.js'; +import { IRemoteUser } from '@/models/entities/user.js'; import { UserPublickey } from '@/models/entities/user-publickey.js'; import { uriPersonCache, userByIdCache } from '@/services/user-cache.js'; import { createPerson } from '@/remote/activitypub/models/person.js'; export type AuthUser = { - user: CacheableRemoteUser; + user: IRemoteUser; key: UserPublickey; }; diff --git a/packages/backend/src/remote/activitypub/models/image.ts b/packages/backend/src/remote/activitypub/models/image.ts index 281cbdf9a..aaf4b90d7 100644 --- a/packages/backend/src/remote/activitypub/models/image.ts +++ b/packages/backend/src/remote/activitypub/models/image.ts @@ -1,5 +1,5 @@ import { uploadFromUrl } from '@/services/drive/upload-from-url.js'; -import { CacheableRemoteUser } from '@/models/entities/user.js'; +import { IRemoteUser } from '@/models/entities/user.js'; import { fetchMeta } from '@/misc/fetch-meta.js'; import { DriveFile } from '@/models/entities/drive-file.js'; import { DriveFiles } from '@/models/index.js'; @@ -11,7 +11,7 @@ import { apLogger } from '../logger.js'; /** * Imageを作成します。 */ -export async function createImage(actor: CacheableRemoteUser, value: any, resolver: Resolver): Promise { +export async function createImage(actor: IRemoteUser, value: any, resolver: Resolver): Promise { // 投稿者が凍結されていたらスキップ if (actor.isSuspended) { throw new Error('actor has been suspended'); @@ -58,7 +58,7 @@ export async function createImage(actor: CacheableRemoteUser, value: any, resolv * If the target Image is registered in FoundKey, return it; otherwise, fetch it from the remote server and return it. * Fetch the image from the remote server, register it in FoundKey and return it. */ -export async function resolveImage(actor: CacheableRemoteUser, value: any, resolver: Resolver): Promise { +export async function resolveImage(actor: IRemoteUser, value: any, resolver: Resolver): Promise { // TODO // Fetch from remote server and register it. diff --git a/packages/backend/src/remote/activitypub/models/mention.ts b/packages/backend/src/remote/activitypub/models/mention.ts index 183ab841a..c42fd197a 100644 --- a/packages/backend/src/remote/activitypub/models/mention.ts +++ b/packages/backend/src/remote/activitypub/models/mention.ts @@ -1,17 +1,17 @@ import promiseLimit from 'promise-limit'; import { toArray, unique } from '@/prelude/array.js'; -import { CacheableUser } from '@/models/entities/user.js'; +import { User } from '@/models/entities/user.js'; import { Resolver } from '@/remote/activitypub/resolver.js'; import { IObject, isMention, IApMention } from '../type.js'; import { resolvePerson } from './person.js'; -export async function extractApMentions(tags: IObject | IObject[] | null | undefined, resolver: Resolver): Promise { +export async function extractApMentions(tags: IObject | IObject[] | null | undefined, resolver: Resolver): Promise { const hrefs = unique(extractApMentionObjects(tags).map(x => x.href as string)); - const limit = promiseLimit(2); + const limit = promiseLimit(2); const mentionedUsers = (await Promise.all( hrefs.map(x => limit(() => resolvePerson(x, resolver).catch(() => null))), - )).filter((x): x is CacheableUser => x != null); + )).filter((x): x is User => x != null); return mentionedUsers; } diff --git a/packages/backend/src/remote/activitypub/models/note.ts b/packages/backend/src/remote/activitypub/models/note.ts index 30245a67f..52de0d49c 100644 --- a/packages/backend/src/remote/activitypub/models/note.ts +++ b/packages/backend/src/remote/activitypub/models/note.ts @@ -2,7 +2,7 @@ import promiseLimit from 'promise-limit'; import config from '@/config/index.js'; import post from '@/services/note/create.js'; -import { CacheableRemoteUser } from '@/models/entities/user.js'; +import { IRemoteUser } from '@/models/entities/user.js'; import { unique, toArray, toSingle } from '@/prelude/array.js'; import { vote } from '@/services/note/polls/vote.js'; import { DriveFile } from '@/models/entities/drive-file.js'; @@ -91,7 +91,7 @@ export async function createNote(value: string | IObject, resolver: Resolver, si apLogger.info(`Creating the Note: ${note.id}`); // 投稿者をフェッチ - const actor = await resolvePerson(getOneApId(note.attributedTo), resolver) as CacheableRemoteUser; + const actor = await resolvePerson(getOneApId(note.attributedTo), resolver) as IRemoteUser; // 投稿者が凍結されていたらスキップ if (actor.isSuspended) { diff --git a/packages/backend/src/remote/activitypub/models/person.ts b/packages/backend/src/remote/activitypub/models/person.ts index 80993a333..a5e268005 100644 --- a/packages/backend/src/remote/activitypub/models/person.ts +++ b/packages/backend/src/remote/activitypub/models/person.ts @@ -6,7 +6,7 @@ import { registerOrFetchInstanceDoc } from '@/services/register-or-fetch-instanc import { Note } from '@/models/entities/note.js'; import { updateUsertags } from '@/services/update-hashtag.js'; import { Users, Instances, Followings, UserProfiles, UserPublickeys } from '@/models/index.js'; -import { User, IRemoteUser, CacheableUser } from '@/models/entities/user.js'; +import { User, IRemoteUser, User } from '@/models/entities/user.js'; import { Emoji } from '@/models/entities/emoji.js'; import { UserNotePining } from '@/models/entities/user-note-pining.js'; import { genId } from '@/misc/gen-id.js'; @@ -121,7 +121,7 @@ async function validateActor(x: IObject, resolver: Resolver): Promise { * * If the target Person is registered in FoundKey, it is returned. */ -export async function fetchPerson(uri: string): Promise { +export async function fetchPerson(uri: string): Promise { if (typeof uri !== 'string') throw new Error('uri is not string'); const cached = uriPersonCache.get(uri); @@ -394,7 +394,7 @@ export async function updatePerson(value: IObject | string, resolver: Resolver): * If the target Person is registered in FoundKey, return it; otherwise, fetch it from a remote server and return it. * Fetch the person from the remote server, register it in FoundKey, and return it. */ -export async function resolvePerson(uri: string, resolver: Resolver, hint?: IObject): Promise { +export async function resolvePerson(uri: string, resolver: Resolver, hint?: IObject): Promise { if (typeof uri !== 'string') throw new Error('uri is not string'); //#region このサーバーに既に登録されていたらそれを返す diff --git a/packages/backend/src/remote/activitypub/perform.ts b/packages/backend/src/remote/activitypub/perform.ts index 8622d43df..23999213c 100644 --- a/packages/backend/src/remote/activitypub/perform.ts +++ b/packages/backend/src/remote/activitypub/perform.ts @@ -1,11 +1,11 @@ import { DAY } from '@/const.js'; -import { CacheableRemoteUser } from '@/models/entities/user.js'; +import { IRemoteUser } from '@/models/entities/user.js'; import { Resolver } from '@/remote/activitypub/resolver.js'; import { IObject } from './type.js'; import { performActivity } from './kernel/index.js'; import { updatePerson } from './models/person.js'; -export async function perform(actor: CacheableRemoteUser, activity: IObject, resolver: Resolver): Promise { +export async function perform(actor: IRemoteUser, activity: IObject, resolver: Resolver): Promise { await performActivity(actor, activity, resolver); // And while I'm at it, I'll update the remote user information if it's out of date. diff --git a/packages/backend/src/server/api/authenticate.ts b/packages/backend/src/server/api/authenticate.ts index 25e87b75e..9da253526 100644 --- a/packages/backend/src/server/api/authenticate.ts +++ b/packages/backend/src/server/api/authenticate.ts @@ -1,4 +1,4 @@ -import { CacheableLocalUser } from '@/models/entities/user.js'; +import { ILocalUser } from '@/models/entities/user.js'; import { Users, AccessTokens } from '@/models/index.js'; import { AccessToken } from '@/models/entities/access-token.js'; import { userByIdCache, localUserByNativeTokenCache } from '@/services/user-cache.js'; @@ -11,7 +11,7 @@ export class AuthenticationError extends Error { } } -export default async (authorization: string | null | undefined, bodyToken: string | null | undefined): Promise<[CacheableLocalUser | null | undefined, AccessToken | null | undefined]> => { +export default async (authorization: string | null | undefined, bodyToken: string | null | undefined): Promise<[ILocalUser | null | undefined, AccessToken | null | undefined]> => { let maybeToken: string | null = null; // check if there is an authorization header set diff --git a/packages/backend/src/server/api/call.ts b/packages/backend/src/server/api/call.ts index dc0e790bd..89c80f367 100644 --- a/packages/backend/src/server/api/call.ts +++ b/packages/backend/src/server/api/call.ts @@ -1,6 +1,6 @@ import { performance } from 'perf_hooks'; import Koa from 'koa'; -import { CacheableLocalUser } from '@/models/entities/user.js'; +import { ILocalUser } from '@/models/entities/user.js'; import { AccessToken } from '@/models/entities/access-token.js'; import { getIpHash } from '@/misc/get-ip-hash.js'; import { limiter } from './limiter.js'; @@ -8,7 +8,7 @@ import endpoints, { IEndpointMeta } from './endpoints.js'; import { ApiError } from './error.js'; import { apiLogger } from './logger.js'; -export default async (endpoint: string, user: CacheableLocalUser | null | undefined, token: AccessToken | null | undefined, data: any, ctx?: Koa.Context) => { +export default async (endpoint: string, user: ILocalUser | null | undefined, token: AccessToken | null | undefined, data: any, ctx?: Koa.Context) => { const isSecure = user != null && token == null; const isModerator = user != null && (user.isModerator || user.isAdmin); diff --git a/packages/backend/src/server/api/define.ts b/packages/backend/src/server/api/define.ts index 243b105ae..811a10ae3 100644 --- a/packages/backend/src/server/api/define.ts +++ b/packages/backend/src/server/api/define.ts @@ -1,6 +1,6 @@ import * as fs from 'node:fs'; import Ajv from 'ajv'; -import { CacheableLocalUser } from '@/models/entities/user.js'; +import { ILocalUser } from '@/models/entities/user.js'; import { Schema, SchemaType } from '@/misc/schema.js'; import { AccessToken } from '@/models/entities/access-token.js'; import { IEndpointMeta } from './endpoints.js'; @@ -10,7 +10,7 @@ export type Response = Record | void; // TODO: paramsの型をT['params']のスキーマ定義から推論する type executor = - (params: SchemaType, user: T['requireCredential'] extends true ? CacheableLocalUser : CacheableLocalUser | null, token: AccessToken | null, file?: any, cleanup?: () => any) => + (params: SchemaType, user: T['requireCredential'] extends true ? ILocalUser : ILocalUser | null, token: AccessToken | null, file?: any, cleanup?: () => any) => Promise>>; const ajv = new Ajv({ @@ -20,10 +20,10 @@ const ajv = new Ajv({ ajv.addFormat('misskey:id', /^[a-zA-Z0-9]+$/); export default function (meta: T, paramDef: Ps, cb: executor) - : (params: any, user: T['requireCredential'] extends true ? CacheableLocalUser : CacheableLocalUser | null, token: AccessToken | null, file?: any) => Promise { + : (params: any, user: T['requireCredential'] extends true ? ILocalUser : ILocalUser | null, token: AccessToken | null, file?: any) => Promise { const validate = ajv.compile(paramDef); - return (params: any, user: T['requireCredential'] extends true ? CacheableLocalUser : CacheableLocalUser | null, token: AccessToken | null, file?: any) => { + return (params: any, user: T['requireCredential'] extends true ? ILocalUser : ILocalUser | null, token: AccessToken | null, file?: any) => { function cleanup() { fs.unlink(file.path, () => {}); } diff --git a/packages/backend/src/server/api/endpoints/ap/show.ts b/packages/backend/src/server/api/endpoints/ap/show.ts index 9492cc60a..31b0f1266 100644 --- a/packages/backend/src/server/api/endpoints/ap/show.ts +++ b/packages/backend/src/server/api/endpoints/ap/show.ts @@ -5,7 +5,7 @@ import { Resolver } from '@/remote/activitypub/resolver.js'; import { extractDbHost } from '@/misc/convert-host.js'; import { Users, Notes } from '@/models/index.js'; import { Note } from '@/models/entities/note.js'; -import { CacheableLocalUser, User } from '@/models/entities/user.js'; +import { ILocalUser, User } from '@/models/entities/user.js'; import { isActor, isPost } from '@/remote/activitypub/type.js'; import { SchemaType } from '@/misc/schema.js'; import { HOUR } from '@/const.js'; @@ -85,7 +85,7 @@ export default define(meta, paramDef, async (ps, me) => { /*** * URIからUserかNoteを解決する */ -async function fetchAny(uri: string, me: CacheableLocalUser | null | undefined): Promise | null> { +async function fetchAny(uri: string, me: ILocalUser | null | undefined): Promise | null> { // Stop if the host is blocked. const host = extractDbHost(uri); if (await shouldBlockInstance(host)) { @@ -122,7 +122,7 @@ async function fetchAny(uri: string, me: CacheableLocalUser | null | undefined): ); } -async function mergePack(me: CacheableLocalUser | null | undefined, user: User | null | undefined, note: Note | null | undefined): Promise | null> { +async function mergePack(me: ILocalUser | null | undefined, user: User | null | undefined, note: Note | null | undefined): Promise | null> { if (user != null) { return { type: 'User', diff --git a/packages/backend/src/services/blocking/delete.ts b/packages/backend/src/services/blocking/delete.ts index 82f92f05a..c26f1ac54 100644 --- a/packages/backend/src/services/blocking/delete.ts +++ b/packages/backend/src/services/blocking/delete.ts @@ -2,13 +2,13 @@ import { renderActivity } from '@/remote/activitypub/renderer/index.js'; import { renderBlock } from '@/remote/activitypub/renderer/block.js'; import renderUndo from '@/remote/activitypub/renderer/undo.js'; import { deliver } from '@/queue/index.js'; -import { CacheableUser } from '@/models/entities/user.js'; +import { User } from '@/models/entities/user.js'; import { Blockings, Users } from '@/models/index.js'; import Logger from '../logger.js'; const logger = new Logger('blocking/delete'); -export default async function(blocker: CacheableUser, blockee: CacheableUser) { +export default async function(blocker: User, blockee: User) { const blocking = await Blockings.findOneBy({ blockerId: blocker.id, blockeeId: blockee.id, diff --git a/packages/backend/src/services/messages/create.ts b/packages/backend/src/services/messages/create.ts index 4a0ea53a8..dd31d8794 100644 --- a/packages/backend/src/services/messages/create.ts +++ b/packages/backend/src/services/messages/create.ts @@ -1,5 +1,5 @@ import { Not } from 'typeorm'; -import { CacheableUser, User } from '@/models/entities/user.js'; +import { User } from '@/models/entities/user.js'; import { UserGroup } from '@/models/entities/user-group.js'; import { DriveFile } from '@/models/entities/drive-file.js'; import { MessagingMessages, UserGroupJoinings, Mutings, Users } from '@/models/index.js'; @@ -13,7 +13,7 @@ import renderCreate from '@/remote/activitypub/renderer/create.js'; import { renderActivity } from '@/remote/activitypub/renderer/index.js'; import { deliver } from '@/queue/index.js'; -export async function createMessage(user: { id: User['id']; host: User['host']; }, recipientUser: CacheableUser | undefined, recipientGroup: UserGroup | undefined, text: string | null | undefined, file: DriveFile | null, uri?: string) { +export async function createMessage(user: { id: User['id']; host: User['host']; }, recipientUser: User | undefined, recipientGroup: UserGroup | undefined, text: string | null | undefined, file: DriveFile | null, uri?: string) { const message = { id: genId(), createdAt: new Date(), diff --git a/packages/backend/src/services/note/polls/vote.ts b/packages/backend/src/services/note/polls/vote.ts index b86e7107d..cdd27da50 100644 --- a/packages/backend/src/services/note/polls/vote.ts +++ b/packages/backend/src/services/note/polls/vote.ts @@ -1,12 +1,12 @@ import { ArrayOverlap, Not } from 'typeorm'; import { publishNoteStream } from '@/services/stream.js'; -import { CacheableUser } from '@/models/entities/user.js'; +import { User } from '@/models/entities/user.js'; import { Note } from '@/models/entities/note.js'; import { PollVotes, NoteWatchings, Polls, Blockings, NoteThreadMutings } from '@/models/index.js'; import { genId } from '@/misc/gen-id.js'; import { createNotification } from '@/services/create-notification.js'; -export async function vote(user: CacheableUser, note: Note, choice: number): Promise { +export async function vote(user: User, note: Note, choice: number): Promise { const poll = await Polls.findOneBy({ noteId: note.id }); if (poll == null) throw new Error('poll not found'); diff --git a/packages/backend/src/services/user-cache.ts b/packages/backend/src/services/user-cache.ts index b4bbb6f1a..77c5d0541 100644 --- a/packages/backend/src/services/user-cache.ts +++ b/packages/backend/src/services/user-cache.ts @@ -1,5 +1,5 @@ import { IsNull } from 'typeorm'; -import { CacheableLocalUser, ILocalUser, User } from '@/models/entities/user.js'; +import { ILocalUser, User } from '@/models/entities/user.js'; import { Users } from '@/models/index.js'; import { Cache } from '@/misc/cache.js'; import { subscriber } from '@/db/redis.js'; @@ -8,7 +8,7 @@ export const userByIdCache = new Cache( Infinity, async (id) => await Users.findOneBy({ id, isDeleted: false }) ?? undefined, ); -export const localUserByNativeTokenCache = new Cache( +export const localUserByNativeTokenCache = new Cache( Infinity, async (token) => await Users.findOneBy({ token, host: IsNull(), isDeleted: false }) as ILocalUser | null ?? undefined, ); From 54d69ed49ee4c0d0db57a94a6019b556b46a1ddd Mon Sep 17 00:00:00 2001 From: Johann150 Date: Sun, 3 Oct 2021 12:20:48 +0200 Subject: [PATCH 21/36] reduce opacity of small tag only once fixes https://github.com/misskey-dev/misskey/issues/7852 --- .../src/components/global/misskey-flavored-markdown.vue | 8 ++++++++ packages/client/src/components/mfm.ts | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/client/src/components/global/misskey-flavored-markdown.vue b/packages/client/src/components/global/misskey-flavored-markdown.vue index 72ab8b9ce..eb0c5f34b 100644 --- a/packages/client/src/components/global/misskey-flavored-markdown.vue +++ b/packages/client/src/components/global/misskey-flavored-markdown.vue @@ -56,6 +56,14 @@ withDefaults(defineProps<{ } } +._mfm_small_ { + opacity: 0.7; +} + +._mfm_small_ ._mfm_small_{ + opacity: initial; +} + @keyframes mfm-spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } diff --git a/packages/client/src/components/mfm.ts b/packages/client/src/components/mfm.ts index 1107edcd6..64fe5bdb5 100644 --- a/packages/client/src/components/mfm.ts +++ b/packages/client/src/components/mfm.ts @@ -197,7 +197,7 @@ export default defineComponent({ case 'small': { return h('small', { - style: 'opacity: 0.7;', + class: '_mfm_small_' }, genEl(token.children)); } From c5327f74d42a811e4a924b0f72b8cd8aaa0d0b71 Mon Sep 17 00:00:00 2001 From: Johann150 Date: Thu, 18 May 2023 21:41:32 +0200 Subject: [PATCH 22/36] refactor to check log levels This will eventually allow the log level to be configured. For now, the explicit debug flag does not work. --- packages/backend/src/services/logger.ts | 112 ++++++++++++++++-------- 1 file changed, 77 insertions(+), 35 deletions(-) diff --git a/packages/backend/src/services/logger.ts b/packages/backend/src/services/logger.ts index a3bfc8a0a..ea5d321fa 100644 --- a/packages/backend/src/services/logger.ts +++ b/packages/backend/src/services/logger.ts @@ -12,7 +12,14 @@ type Domain = { color?: KEYWORD; }; -type Level = 'error' | 'success' | 'warning' | 'debug' | 'info'; +export const LEVELS = { + error: 0, + warning: 1, + success: 2, + info: 3, + debug: 4, +}; +export type Level = LEVELS[keyof LEVELS]; /** * Class that facilitates recording log messages to the console and optionally a syslog server. @@ -22,6 +29,10 @@ export default class Logger { private parentLogger: Logger | null = null; private store: boolean; private syslogClient: SyslogPro.RFC5424 | null = null; + /** + * Messages below this level will be discarded. + */ + private minLevel: Level; /** * Create a logger instance. @@ -29,12 +40,13 @@ export default class Logger { * @param color Log message color * @param store Whether to store messages */ - constructor(domain: string, color?: KEYWORD, store = true) { + constructor(domain: string, color?: KEYWORD, store = true, minLevel: Level = LEVELS.info) { this.domain = { name: domain, color, }; this.store = store; + this.minLevel = minLevel; if (config.syslog) { this.syslogClient = new SyslogPro.RFC5424({ @@ -58,16 +70,29 @@ export default class Logger { * @param store Whether to store messages * @returns A Logger instance whose parent logger is this instance. */ - public createSubLogger(domain: string, color?: KEYWORD, store = true): Logger { - const logger = new Logger(domain, color, store); + public createSubLogger(domain: string, color?: KEYWORD, store = true, minLevel: Level = LEVELS.info): Logger { + const logger = new Logger(domain, color, store, minLevel); logger.parentLogger = this; return logger; } + /** + * Log a message. + * @param level Indicates the level of this particular message. If it is + * less than the minimum level configured, the message will be discarded. + * @param message The message to be logged. + * @param important Whether to highlight this message as especially important. + * @param subDomains Names of sub-loggers to be added. + */ private log(level: Level, message: string, data?: Record | null, important = false, subDomains: Domain[] = [], _store = true): void { if (envOption.quiet) return; - const store = _store && this.store && (level !== 'debug'); + const store = _store && this.store; + // Check against the configured log level. + if (level < this.minLevel) return; + + // If this logger has a parent logger, delegate the actual logging to it, + // so the parent domain(s) will be logged properly. if (this.parentLogger) { this.parentLogger.log(level, message, data, important, [this.domain].concat(subDomains), store); return; @@ -75,34 +100,53 @@ export default class Logger { const time = dateFormat(new Date(), 'HH:mm:ss'); const worker = cluster.isPrimary ? '*' : cluster.worker?.id; - const l = - level === 'error' ? important ? chalk.bgRed.white('ERR ') : chalk.red('ERR ') : - level === 'warning' ? chalk.yellow('WARN') : - level === 'success' ? important ? chalk.bgGreen.white('DONE') : chalk.green('DONE') : - level === 'debug' ? chalk.gray('VERB') : - chalk.blue('INFO'); const domains = [this.domain].concat(subDomains).map(d => d.color ? chalk.rgb(...convertColor.keyword.rgb(d.color))(d.name) : chalk.white(d.name)); - const m = - level === 'error' ? chalk.red(message) : - level === 'warning' ? chalk.yellow(message) : - level === 'success' ? chalk.green(message) : - level === 'debug' ? chalk.gray(message) : - message; - let log = `${l} ${worker}\t[${domains.join(' ')}]\t${m}`; + let levelDisplay; + let messageDisplay; + switch (level) { + case LEVELS.error: + if (important) { + levelDisplay = chalk.bgRed.white('ERR '); + } else { + levelDisplay = chalk.red('ERR '); + } + messageDisplay = chalk.red(message); + break; + case LEVELS.warning: + levelDisplay = chalk.yellow('WARN'); + messageDisplay = chalk.yellow(message); + break; + case LEVELS.success: + if (important) { + levelDisplay = chalk.bgGreen.white('DONE'); + } else { + levelDisplay = chalk.green('DONE'); + } + messageDisplay = chalk.green(message); + break; + case LEVELS.info: + levelDisplay = chalk.blue('INFO'); + messageDisplay = message; + break; + case LEVELS.debug: default: + levelDisplay = chalk.gray('VERB'); + messageDisplay = chalk.gray(message); + break; + } + + let log = `${levelDisplay} ${worker}\t[${domains.join(' ')}]\t${messageDisplay}`; if (envOption.withLogTime) log = chalk.gray(time) + ' ' + log; console.log(important ? chalk.bold(log) : log); - if (store) { - if (this.syslogClient) { - const send = - level === 'error' ? this.syslogClient.error : - level === 'warning' ? this.syslogClient.warning : - this.syslogClient.info; + if (store && this.syslogClient) { + const send = + level === LEVELS.error ? this.syslogClient.error : + level === LEVELS.warning ? this.syslogClient.warning : + this.syslogClient.info; - send.bind(this.syslogClient)(message).catch(() => {}); - } + send.bind(this.syslogClient)(message).catch(() => {}); } } @@ -116,11 +160,11 @@ export default class Logger { public error(err: string | Error, data: Record = {}, important = false): void { if (err instanceof Error) { data.e = err; - this.log('error', err.toString(), data, important); + this.log(LEVELS.error, err.toString(), data, important); } else if (typeof err === 'object') { - this.log('error', `${(err as any).message || (err as any).name || err}`, data, important); + this.log(LEVELS.error, `${(err as any).message || (err as any).name || err}`, data, important); } else { - this.log('error', `${err}`, data, important); + this.log(LEVELS.error, `${err}`, data, important); } } @@ -132,7 +176,7 @@ export default class Logger { * @param important Whether this warning is important */ public warn(message: string, data?: Record | null, important = false): void { - this.log('warning', message, data, important); + this.log(LEVELS.warning, message, data, important); } /** @@ -143,7 +187,7 @@ export default class Logger { * @param important Whether this success message is important */ public succ(message: string, data?: Record | null, important = false): void { - this.log('success', message, data, important); + this.log(LEVELS.success, message, important); } /** @@ -154,9 +198,7 @@ export default class Logger { * @param important Whether this debug message is important */ public debug(message: string, data?: Record | null, important = false): void { - if (process.env.NODE_ENV !== 'production' || envOption.verbose) { - this.log('debug', message, data, important); - } + this.log(LEVELS.debug, message, data, important); } /** @@ -167,6 +209,6 @@ export default class Logger { * @param important Whether this info message is important */ public info(message: string, data?: Record | null, important = false): void { - this.log('info', message, data, important); + this.log(LEVELS.info, message, data, important); } } From 85a392ee335c941fd2d27caf5086efaa77c3b34d Mon Sep 17 00:00:00 2001 From: Johann150 Date: Thu, 18 May 2023 22:42:30 +0200 Subject: [PATCH 23/36] logger: remove unused structured data The `data` field is not used anywhere in the logger. While it would be possible to send structured data through syslog, it seems unnecessary at present and also the way in which this structured data would have to be provided sounds too cumbersome to implement for no real value. --- packages/backend/src/boot/master.ts | 12 +++---- packages/backend/src/queue/index.ts | 20 ++++++------ .../src/remote/activitypub/models/note.ts | 8 +---- packages/backend/src/server/api/call.ts | 10 +----- .../src/services/drive/upload-from-url.ts | 5 +-- packages/backend/src/services/logger.ts | 32 ++++++++----------- 6 files changed, 32 insertions(+), 55 deletions(-) diff --git a/packages/backend/src/boot/master.ts b/packages/backend/src/boot/master.ts index 6f64715f8..f68a2d310 100644 --- a/packages/backend/src/boot/master.ts +++ b/packages/backend/src/boot/master.ts @@ -41,7 +41,7 @@ function greet(): void { } bootLogger.info('Welcome to FoundKey!'); - bootLogger.info(`FoundKey v${meta.version}`, null, true); + bootLogger.info(`FoundKey v${meta.version}`, true); } /** @@ -59,7 +59,7 @@ export async function masterMain(): Promise { config = loadConfigBoot(); await connectDb(); } catch (e) { - bootLogger.error('Fatal error occurred during initialization', {}, true); + bootLogger.error('Fatal error occurred during initialization', true); process.exit(1); } @@ -69,7 +69,7 @@ export async function masterMain(): Promise { await spawnWorkers(config.clusterLimits); } - bootLogger.succ(`Now listening on port ${config.port} on ${config.url}`, null, true); + bootLogger.succ(`Now listening on port ${config.port} on ${config.url}`, true); if (!envOption.noDaemons) { import('../daemons/server-stats.js').then(x => x.serverStats()); @@ -84,7 +84,7 @@ function showEnvironment(): void { if (env !== 'production') { logger.warn('The environment is not in production mode.'); - logger.warn('DO NOT USE FOR PRODUCTION PURPOSE!', {}, true); + logger.warn('DO NOT USE FOR PRODUCTION PURPOSE!', true); } } @@ -109,7 +109,7 @@ function loadConfigBoot(): Config { } catch (exception) { const e = exception as Partial | Error; if ('code' in e && e.code === 'ENOENT') { - configLogger.error('Configuration file not found', {}, true); + configLogger.error('Configuration file not found', true); process.exit(1); } else if (e instanceof Error) { configLogger.error(e.message); @@ -133,7 +133,7 @@ async function connectDb(): Promise { const v = await db.query('SHOW server_version').then(x => x[0].server_version); dbLogger.succ(`Connected: v${v}`); } catch (e) { - dbLogger.error('Cannot connect', {}, true); + dbLogger.error('Cannot connect', true); dbLogger.error(e as Error | string); process.exit(1); } diff --git a/packages/backend/src/queue/index.ts b/packages/backend/src/queue/index.ts index cc125a3de..558f54ff0 100644 --- a/packages/backend/src/queue/index.ts +++ b/packages/backend/src/queue/index.ts @@ -39,8 +39,8 @@ systemQueue .on('waiting', (jobId) => systemLogger.debug(`waiting id=${jobId}`)) .on('active', (job) => systemLogger.debug(`active id=${job.id}`)) .on('completed', (job, result) => systemLogger.debug(`completed(${result}) id=${job.id}`)) - .on('failed', (job, err) => systemLogger.warn(`failed(${err}) id=${job.id}`, { job, e: renderError(err) })) - .on('error', (job: any, err: Error) => systemLogger.error(`error ${err}`, { job, e: renderError(err) })) + .on('failed', (job, err) => systemLogger.warn(`failed(${err}) id=${job.id}`)) + .on('error', (job: any, err: Error) => systemLogger.error(`error ${err}`)) .on('stalled', (job) => systemLogger.warn(`stalled id=${job.id}`)); deliverQueue @@ -48,31 +48,31 @@ deliverQueue .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('error', (job: any, err: Error) => deliverLogger.error(`error ${err}`, { job, e: renderError(err) })) + .on('error', (job: any, err: Error) => deliverLogger.error(`error ${err}`)) .on('stalled', (job) => deliverLogger.warn(`stalled ${getJobInfo(job)} to=${job.data.to}`)); inboxQueue .on('waiting', (jobId) => inboxLogger.debug(`waiting id=${jobId}`)) .on('active', (job) => inboxLogger.debug(`active ${getJobInfo(job, true)}`)) .on('completed', (job, result) => inboxLogger.debug(`completed(${result}) ${getJobInfo(job, true)}`)) - .on('failed', (job, err) => inboxLogger.warn(`failed(${err}) ${getJobInfo(job)} activity=${job.data.activity ? job.data.activity.id : 'none'}`, { job, e: renderError(err) })) - .on('error', (job: any, err: Error) => inboxLogger.error(`error ${err}`, { job, e: renderError(err) })) + .on('failed', (job, err) => inboxLogger.warn(`failed(${err}) ${getJobInfo(job)} activity=${job.data.activity ? job.data.activity.id : 'none'}`)) + .on('error', (job: any, err: Error) => inboxLogger.error(`error ${err}`)) .on('stalled', (job) => inboxLogger.warn(`stalled ${getJobInfo(job)} activity=${job.data.activity ? job.data.activity.id : 'none'}`)); dbQueue .on('waiting', (jobId) => dbLogger.debug(`waiting id=${jobId}`)) .on('active', (job) => dbLogger.debug(`active id=${job.id}`)) .on('completed', (job, result) => dbLogger.debug(`completed(${result}) id=${job.id}`)) - .on('failed', (job, err) => dbLogger.warn(`failed(${err}) id=${job.id}`, { job, e: renderError(err) })) - .on('error', (job: any, err: Error) => dbLogger.error(`error ${err}`, { job, e: renderError(err) })) + .on('failed', (job, err) => dbLogger.warn(`failed(${err}) id=${job.id}`)) + .on('error', (job: any, err: Error) => dbLogger.error(`error ${err}`)) .on('stalled', (job) => dbLogger.warn(`stalled id=${job.id}`)); objectStorageQueue .on('waiting', (jobId) => objectStorageLogger.debug(`waiting id=${jobId}`)) .on('active', (job) => objectStorageLogger.debug(`active id=${job.id}`)) .on('completed', (job, result) => objectStorageLogger.debug(`completed(${result}) id=${job.id}`)) - .on('failed', (job, err) => objectStorageLogger.warn(`failed(${err}) id=${job.id}`, { job, e: renderError(err) })) - .on('error', (job: any, err: Error) => objectStorageLogger.error(`error ${err}`, { job, e: renderError(err) })) + .on('failed', (job, err) => objectStorageLogger.warn(`failed(${err}) id=${job.id}`)) + .on('error', (job: any, err: Error) => objectStorageLogger.error(`error ${err}`)) .on('stalled', (job) => objectStorageLogger.warn(`stalled id=${job.id}`)); webhookDeliverQueue @@ -80,7 +80,7 @@ webhookDeliverQueue .on('active', (job) => webhookLogger.debug(`active ${getJobInfo(job, true)} to=${job.data.to}`)) .on('completed', (job, result) => webhookLogger.debug(`completed(${result}) ${getJobInfo(job, true)} to=${job.data.to}`)) .on('failed', (job, err) => webhookLogger.warn(`failed(${err}) ${getJobInfo(job)} to=${job.data.to}`)) - .on('error', (job: any, err: Error) => webhookLogger.error(`error ${err}`, { job, e: renderError(err) })) + .on('error', (job: any, err: Error) => webhookLogger.error(`error ${err}`)) .on('stalled', (job) => webhookLogger.warn(`stalled ${getJobInfo(job)} to=${job.data.to}`)); export function deliver(user: ThinUser, content: unknown, to: string | null) { diff --git a/packages/backend/src/remote/activitypub/models/note.ts b/packages/backend/src/remote/activitypub/models/note.ts index 52de0d49c..3607ad62b 100644 --- a/packages/backend/src/remote/activitypub/models/note.ts +++ b/packages/backend/src/remote/activitypub/models/note.ts @@ -74,13 +74,7 @@ export async function createNote(value: string | IObject, resolver: Resolver, si const err = validateNote(object); if (err) { - apLogger.error(`${err.message}`, { - resolver: { - history: resolver.getHistory(), - }, - value, - object, - }); + apLogger.error(`${err.message}`); throw new Error('invalid note'); } diff --git a/packages/backend/src/server/api/call.ts b/packages/backend/src/server/api/call.ts index 89c80f367..7ecb0676b 100644 --- a/packages/backend/src/server/api/call.ts +++ b/packages/backend/src/server/api/call.ts @@ -82,15 +82,7 @@ export default async (endpoint: string, user: ILocalUser | null | undefined, tok if (e instanceof ApiError) { throw e; } else { - apiLogger.error(`Internal error occurred in ${ep.name}: ${e.message}`, { - ep: ep.name, - ps: data, - e: { - message: e.message, - code: e.name, - stack: e.stack, - }, - }); + apiLogger.error(`Internal error occurred in ${ep.name}: ${e.message}`); throw new ApiError('INTERNAL_ERROR', { e: { message: e.message, diff --git a/packages/backend/src/services/drive/upload-from-url.ts b/packages/backend/src/services/drive/upload-from-url.ts index e8f3793b7..912ac25f6 100644 --- a/packages/backend/src/services/drive/upload-from-url.ts +++ b/packages/backend/src/services/drive/upload-from-url.ts @@ -60,10 +60,7 @@ export async function uploadFromUrl({ logger.succ(`Got: ${driveFile.id}`); return driveFile; } catch (e) { - logger.error(`Failed to create drive file: ${e}`, { - url, - e, - }); + logger.error(`Failed to create drive file: ${e}`); throw e; } finally { cleanup(); diff --git a/packages/backend/src/services/logger.ts b/packages/backend/src/services/logger.ts index ea5d321fa..7b0d1ce2b 100644 --- a/packages/backend/src/services/logger.ts +++ b/packages/backend/src/services/logger.ts @@ -84,7 +84,7 @@ export default class Logger { * @param important Whether to highlight this message as especially important. * @param subDomains Names of sub-loggers to be added. */ - private log(level: Level, message: string, data?: Record | null, important = false, subDomains: Domain[] = [], _store = true): void { + private log(level: Level, message: string, important = false, subDomains: Domain[] = [], _store = true): void { if (envOption.quiet) return; const store = _store && this.store; @@ -94,7 +94,7 @@ export default class Logger { // If this logger has a parent logger, delegate the actual logging to it, // so the parent domain(s) will be logged properly. if (this.parentLogger) { - this.parentLogger.log(level, message, data, important, [this.domain].concat(subDomains), store); + this.parentLogger.log(level, message, important, [this.domain].concat(subDomains), store); return; } @@ -154,17 +154,15 @@ export default class Logger { * Log an error message. * Use in situations where execution cannot be continued. * @param err Error or string containing an error message - * @param data Data relating to the error * @param important Whether this error is important */ - public error(err: string | Error, data: Record = {}, important = false): void { + public error(err: string | Error, important = false): void { if (err instanceof Error) { - data.e = err; - this.log(LEVELS.error, err.toString(), data, important); + this.log(LEVELS.error, err.toString(), important); } else if (typeof err === 'object') { - this.log(LEVELS.error, `${(err as any).message || (err as any).name || err}`, data, important); + this.log(LEVELS.error, `${(err as any).message || (err as any).name || err}`, important); } else { - this.log(LEVELS.error, `${err}`, data, important); + this.log(LEVELS.error, `${err}`, important); } } @@ -172,21 +170,19 @@ export default class Logger { * Log a warning message. * Use in situations where execution can continue but needs to be improved. * @param message Warning message - * @param data Data relating to the warning * @param important Whether this warning is important */ - public warn(message: string, data?: Record | null, important = false): void { - this.log(LEVELS.warning, message, data, important); + public warn(message: string, important = false): void { + this.log(LEVELS.warning, message, important); } /** * Log a success message. * Use in situations where something has been successfully done. * @param message Success message - * @param data Data relating to the success * @param important Whether this success message is important */ - public succ(message: string, data?: Record | null, important = false): void { + public succ(message: string, important = false): void { this.log(LEVELS.success, message, important); } @@ -194,21 +190,19 @@ export default class Logger { * Log a debug message. * Use for debugging (information needed by developers but not required by users). * @param message Debug message - * @param data Data relating to the debug message * @param important Whether this debug message is important */ - public debug(message: string, data?: Record | null, important = false): void { - this.log(LEVELS.debug, message, data, important); + public debug(message: string, important = false): void { + this.log(LEVELS.debug, message, important); } /** * Log an informational message. * Use when something needs to be logged but doesn't fit into other levels. * @param message Info message - * @param data Data relating to the info message * @param important Whether this info message is important */ - public info(message: string, data?: Record | null, important = false): void { - this.log(LEVELS.info, message, data, important); + public info(message: string, important = false): void { + this.log(LEVELS.info, message, important); } } From d6452795b0de2766a1f77208d538233c30f748a1 Mon Sep 17 00:00:00 2001 From: Johann150 Date: Fri, 19 May 2023 00:21:47 +0200 Subject: [PATCH 24/36] translate more comments --- .../backend/src/queue/processors/db/export-custom-emojis.ts | 2 +- packages/backend/src/server/file/send-drive-file.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/backend/src/queue/processors/db/export-custom-emojis.ts b/packages/backend/src/queue/processors/db/export-custom-emojis.ts index f31531db4..9aa503e43 100644 --- a/packages/backend/src/queue/processors/db/export-custom-emojis.ts +++ b/packages/backend/src/queue/processors/db/export-custom-emojis.ts @@ -71,7 +71,7 @@ export async function exportCustomEmojis(job: Bull.Job, done: () => void): Promi try { await downloadUrl(emoji.originalUrl, emojiPath); downloaded = true; - } catch (e) { // TODO: 何度か再試行 + } catch (e) { // TODO: retry logger.error(e instanceof Error ? e : new Error(e as string)); } diff --git a/packages/backend/src/server/file/send-drive-file.ts b/packages/backend/src/server/file/send-drive-file.ts index 0ae16d94c..f5934540a 100644 --- a/packages/backend/src/server/file/send-drive-file.ts +++ b/packages/backend/src/server/file/send-drive-file.ts @@ -49,7 +49,7 @@ export default async function(ctx: Koa.Context) { const isWebpublic = file.webpublicAccessKey === key; if (!file.storedInternal) { - if (file.isLink && file.uri) { // 期限切れリモートファイル + if (file.isLink && file.uri) { // expired remote file const [path, cleanup] = await createTemp(); try { From 410c519953d662e51806d4111bc71914511bc3d5 Mon Sep 17 00:00:00 2001 From: Johann150 Date: Fri, 19 May 2023 00:32:11 +0200 Subject: [PATCH 25/36] remove some default exports --- packages/backend/src/boot/master.ts | 2 +- packages/backend/src/config/load.ts | 2 +- packages/backend/src/server/file/index.ts | 2 +- packages/backend/src/server/file/send-drive-file.ts | 3 +-- packages/backend/src/server/web/feed.ts | 2 +- packages/backend/src/server/web/index.ts | 2 +- packages/backend/src/services/stream.ts | 4 +--- 7 files changed, 7 insertions(+), 10 deletions(-) diff --git a/packages/backend/src/boot/master.ts b/packages/backend/src/boot/master.ts index f68a2d310..7a27deb9a 100644 --- a/packages/backend/src/boot/master.ts +++ b/packages/backend/src/boot/master.ts @@ -8,7 +8,7 @@ import chalkTemplate from 'chalk-template'; import semver from 'semver'; import Logger from '@/services/logger.js'; -import loadConfig from '@/config/load.js'; +import { loadConfig } from '@/config/load.js'; import { Config } from '@/config/types.js'; import { showMachineInfo } from '@/misc/show-machine-info.js'; import { envOption } from '@/env.js'; diff --git a/packages/backend/src/config/load.ts b/packages/backend/src/config/load.ts index a6431162d..f16fb850b 100644 --- a/packages/backend/src/config/load.ts +++ b/packages/backend/src/config/load.ts @@ -23,7 +23,7 @@ const path = process.env.NODE_ENV === 'test' ? `${dir}/test.yml` : `${dir}/default.yml`; -export default function load(): Config { +export function loadConfig(): 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')); let config = yaml.load(fs.readFileSync(path, 'utf-8')) as Source; diff --git a/packages/backend/src/server/file/index.ts b/packages/backend/src/server/file/index.ts index 4c4707e61..56bf14f9a 100644 --- a/packages/backend/src/server/file/index.ts +++ b/packages/backend/src/server/file/index.ts @@ -8,7 +8,7 @@ import { dirname } from 'node:path'; import Koa from 'koa'; import cors from '@koa/cors'; import Router from '@koa/router'; -import sendDriveFile from './send-drive-file.js'; +import { sendDriveFile } from './send-drive-file.js'; const _filename = fileURLToPath(import.meta.url); const _dirname = dirname(_filename); diff --git a/packages/backend/src/server/file/send-drive-file.ts b/packages/backend/src/server/file/send-drive-file.ts index f5934540a..990dd98fe 100644 --- a/packages/backend/src/server/file/send-drive-file.ts +++ b/packages/backend/src/server/file/send-drive-file.ts @@ -27,8 +27,7 @@ const commonReadableHandlerGenerator = (ctx: Koa.Context) => (e: Error): void => ctx.set('Cache-Control', 'max-age=300'); }; -// eslint-disable-next-line import/no-default-export -export default async function(ctx: Koa.Context) { +export async function sendDriveFile(ctx: Koa.Context) { const key = ctx.params.key; // Fetch drive file diff --git a/packages/backend/src/server/web/feed.ts b/packages/backend/src/server/web/feed.ts index b83ccf188..497d677c6 100644 --- a/packages/backend/src/server/web/feed.ts +++ b/packages/backend/src/server/web/feed.ts @@ -4,7 +4,7 @@ import config from '@/config/index.js'; import { User } from '@/models/entities/user.js'; import { Notes, DriveFiles, UserProfiles, Users } from '@/models/index.js'; -export default async function(user: User) { +export async function packFeed(user: User) { const author = { link: `${config.url}/@${user.username}`, name: user.name || user.username, diff --git a/packages/backend/src/server/web/index.ts b/packages/backend/src/server/web/index.ts index 7fe462d43..7c04077d4 100644 --- a/packages/backend/src/server/web/index.ts +++ b/packages/backend/src/server/web/index.ts @@ -26,7 +26,7 @@ import { MINUTE, DAY } from '@/const.js'; import { genOpenapiSpec } from '../api/openapi/gen-spec.js'; import { urlPreviewHandler } from './url-preview.js'; import { manifestHandler } from './manifest.js'; -import packFeed from './feed.js'; +import { packFeed } from './feed.js'; const _filename = fileURLToPath(import.meta.url); const _dirname = dirname(_filename); diff --git a/packages/backend/src/services/stream.ts b/packages/backend/src/services/stream.ts index 0119d7fdf..87ddee19d 100644 --- a/packages/backend/src/services/stream.ts +++ b/packages/backend/src/services/stream.ts @@ -95,9 +95,7 @@ class Publisher { }; } -const publisher = new Publisher(); - -export default publisher; +export const publisher = new Publisher(); export const publishInternalEvent = publisher.publishInternalEvent; export const publishUserEvent = publisher.publishUserEvent; From b0d8e15796bcbe463e170e98e5d52f2337802d58 Mon Sep 17 00:00:00 2001 From: Johann150 Date: Fri, 19 May 2023 20:07:26 +0200 Subject: [PATCH 26/36] fixup: remove some default exports This is a fixup for commit 410c519953d662e51806d4111bc71914511bc3d5. --- packages/backend/src/config/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/backend/src/config/index.ts b/packages/backend/src/config/index.ts index 3e53b0003..6b407f269 100644 --- a/packages/backend/src/config/index.ts +++ b/packages/backend/src/config/index.ts @@ -1,3 +1,3 @@ -import load from './load.js'; +import { loadConfig } from './load.js'; -export default load(); +export default loadConfig(); From 2c3f731ae245eef97f1893f904b103da005c877e Mon Sep 17 00:00:00 2001 From: Johann150 Date: Fri, 19 May 2023 23:08:06 +0200 Subject: [PATCH 27/36] remove more default exports --- packages/backend/src/server/api/api-handler.ts | 2 +- packages/backend/src/server/api/authenticate.ts | 4 ++-- packages/backend/src/server/api/call.ts | 2 +- packages/backend/src/server/api/endpoints.ts | 4 +--- packages/backend/src/server/api/endpoints/endpoint.ts | 2 +- packages/backend/src/server/api/endpoints/endpoints.ts | 2 +- packages/backend/src/server/api/index.ts | 2 +- packages/backend/src/server/api/openapi/gen-spec.ts | 2 +- packages/backend/src/server/api/streaming.ts | 2 +- 9 files changed, 10 insertions(+), 12 deletions(-) diff --git a/packages/backend/src/server/api/api-handler.ts b/packages/backend/src/server/api/api-handler.ts index de9af0890..3bde09602 100644 --- a/packages/backend/src/server/api/api-handler.ts +++ b/packages/backend/src/server/api/api-handler.ts @@ -1,7 +1,7 @@ import Koa from 'koa'; import { IEndpoint } from './endpoints.js'; -import authenticate, { AuthenticationError } from './authenticate.js'; +import { authenticate, AuthenticationError } from './authenticate.js'; import call from './call.js'; import { ApiError } from './error.js'; diff --git a/packages/backend/src/server/api/authenticate.ts b/packages/backend/src/server/api/authenticate.ts index 9da253526..7daa91de4 100644 --- a/packages/backend/src/server/api/authenticate.ts +++ b/packages/backend/src/server/api/authenticate.ts @@ -11,7 +11,7 @@ export class AuthenticationError extends Error { } } -export default async (authorization: string | null | undefined, bodyToken: string | null | undefined): Promise<[ILocalUser | null | undefined, AccessToken | null | undefined]> => { +export async function authenticate(authorization: string | null | undefined, bodyToken: string | null | undefined): Promise<[ILocalUser | null | undefined, AccessToken | null | undefined]> { let maybeToken: string | null = null; // check if there is an authorization header set @@ -66,4 +66,4 @@ export default async (authorization: string | null | undefined, bodyToken: strin return [user, accessToken]; } -}; +} diff --git a/packages/backend/src/server/api/call.ts b/packages/backend/src/server/api/call.ts index 7ecb0676b..ea8c6086e 100644 --- a/packages/backend/src/server/api/call.ts +++ b/packages/backend/src/server/api/call.ts @@ -4,7 +4,7 @@ import { ILocalUser } from '@/models/entities/user.js'; import { AccessToken } from '@/models/entities/access-token.js'; import { getIpHash } from '@/misc/get-ip-hash.js'; import { limiter } from './limiter.js'; -import endpoints, { IEndpointMeta } from './endpoints.js'; +import { endpoints, IEndpointMeta } from './endpoints.js'; import { ApiError } from './error.js'; import { apiLogger } from './logger.js'; diff --git a/packages/backend/src/server/api/endpoints.ts b/packages/backend/src/server/api/endpoints.ts index 55763d875..a7757c2b5 100644 --- a/packages/backend/src/server/api/endpoints.ts +++ b/packages/backend/src/server/api/endpoints.ts @@ -713,7 +713,7 @@ export interface IEndpoint { params: Schema; } -const endpoints: IEndpoint[] = eps.map(([name, ep]) => { +export const endpoints: IEndpoint[] = eps.map(([name, ep]) => { return { name, exec: ep.default, @@ -721,5 +721,3 @@ const endpoints: IEndpoint[] = eps.map(([name, ep]) => { params: ep.paramDef, }; }); - -export default endpoints; diff --git a/packages/backend/src/server/api/endpoints/endpoint.ts b/packages/backend/src/server/api/endpoints/endpoint.ts index f380a5287..b5894c10d 100644 --- a/packages/backend/src/server/api/endpoints/endpoint.ts +++ b/packages/backend/src/server/api/endpoints/endpoint.ts @@ -1,5 +1,5 @@ import define from '@/server/api/define.js'; -import endpoints from '@/server/api/endpoints.js'; +import { endpoints } from '@/server/api/endpoints.js'; export const meta = { requireCredential: false, diff --git a/packages/backend/src/server/api/endpoints/endpoints.ts b/packages/backend/src/server/api/endpoints/endpoints.ts index 184f74e79..976109887 100644 --- a/packages/backend/src/server/api/endpoints/endpoints.ts +++ b/packages/backend/src/server/api/endpoints/endpoints.ts @@ -1,5 +1,5 @@ import define from '@/server/api/define.js'; -import endpoints from '@/server/api/endpoints.js'; +import { endpoints } from '@/server/api/endpoints.js'; export const meta = { requireCredential: false, diff --git a/packages/backend/src/server/api/index.ts b/packages/backend/src/server/api/index.ts index fe0c4450a..554596e3a 100644 --- a/packages/backend/src/server/api/index.ts +++ b/packages/backend/src/server/api/index.ts @@ -10,7 +10,7 @@ import cors from '@koa/cors'; import { Instances, AccessTokens, Users } from '@/models/index.js'; import config from '@/config/index.js'; -import endpoints from './endpoints.js'; +import { endpoints } from './endpoints.js'; import { handler } from './api-handler.js'; import signup from './private/signup.js'; import signin from './private/signin.js'; diff --git a/packages/backend/src/server/api/openapi/gen-spec.ts b/packages/backend/src/server/api/openapi/gen-spec.ts index 0a7fcf667..9dc1c89cf 100644 --- a/packages/backend/src/server/api/openapi/gen-spec.ts +++ b/packages/backend/src/server/api/openapi/gen-spec.ts @@ -2,7 +2,7 @@ import config from '@/config/index.js'; import { kinds } from '@/misc/api-permissions.js'; import { I18n } from '@/misc/i18n.js'; import { errors as errorDefinitions } from '@/server/api/error.js'; -import endpoints from '@/server/api/endpoints.js'; +import { endpoints } from '@/server/api/endpoints.js'; import { schemas, convertSchemaToOpenApiSchema } from './schemas.js'; import { httpCodes } from './http-codes.js'; diff --git a/packages/backend/src/server/api/streaming.ts b/packages/backend/src/server/api/streaming.ts index 797443afb..eefbcc216 100644 --- a/packages/backend/src/server/api/streaming.ts +++ b/packages/backend/src/server/api/streaming.ts @@ -6,7 +6,7 @@ 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'; -import authenticate from './authenticate.js'; +import { authenticate } from './authenticate.js'; export const initializeStreamingServer = (server: http.Server): void => { // Init websocket server From ff66f48ea2ddea59550341ed854640b7d5b80b3f Mon Sep 17 00:00:00 2001 From: Johann150 Date: Fri, 19 May 2023 00:59:53 +0200 Subject: [PATCH 28/36] remove syslog closes https://akkoma.dev/FoundKeyGang/FoundKey/issues/19 Changelog: Removed --- .config/example.yml | 5 ----- packages/backend/package.json | 2 -- packages/backend/src/config/types.ts | 5 ----- packages/backend/src/services/logger.ts | 27 +------------------------ yarn.lock | 25 ----------------------- 5 files changed, 1 insertion(+), 63 deletions(-) diff --git a/.config/example.yml b/.config/example.yml index 4146881b1..8d4a162cb 100644 --- a/.config/example.yml +++ b/.config/example.yml @@ -108,11 +108,6 @@ redis: #deliverJobMaxAttempts: 12 #inboxJobMaxAttempts: 8 -# Syslog option -#syslog: -# host: localhost -# port: 514 - # Proxy for HTTP/HTTPS outgoing connections #proxy: http://127.0.0.1:3128 diff --git a/packages/backend/package.json b/packages/backend/package.json index 5d7df7cfa..d9c830baf 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -100,7 +100,6 @@ "stringz": "2.1.0", "style-loader": "3.3.1", "summaly": "2.7.0", - "syslog-pro": "1.0.0", "systeminformation": "5.11.22", "tinycolor2": "1.4.2", "tmp": "0.2.1", @@ -158,7 +157,6 @@ "@types/sinon": "^10.0.13", "@types/sinonjs__fake-timers": "8.1.2", "@types/speakeasy": "2.0.7", - "@types/syslog-pro": "^1.0.0", "@types/tinycolor2": "1.4.3", "@types/tmp": "0.2.3", "@types/uuid": "8.3.4", diff --git a/packages/backend/src/config/types.ts b/packages/backend/src/config/types.ts index 55226ca47..686a8c242 100644 --- a/packages/backend/src/config/types.ts +++ b/packages/backend/src/config/types.ts @@ -59,11 +59,6 @@ export type Source = { deliverJobMaxAttempts?: number; inboxJobMaxAttempts?: number; - syslog?: { - host: string; - port: number; - }; - mediaProxy?: string; proxyRemoteFiles?: boolean; internalStoragePath?: string; diff --git a/packages/backend/src/services/logger.ts b/packages/backend/src/services/logger.ts index 7b0d1ce2b..5c24186b6 100644 --- a/packages/backend/src/services/logger.ts +++ b/packages/backend/src/services/logger.ts @@ -2,7 +2,6 @@ import cluster from 'node:cluster'; import chalk from 'chalk'; import convertColor from 'color-convert'; import { format as dateFormat } from 'date-fns'; -import * as SyslogPro from 'syslog-pro'; import config from '@/config/index.js'; import { envOption } from '@/env.js'; import type { KEYWORD } from 'color-convert/conversions.js'; @@ -22,13 +21,12 @@ export const LEVELS = { export type Level = LEVELS[keyof LEVELS]; /** - * Class that facilitates recording log messages to the console and optionally a syslog server. + * Class that facilitates recording log messages to the console. */ export default class Logger { private domain: Domain; private parentLogger: Logger | null = null; private store: boolean; - private syslogClient: SyslogPro.RFC5424 | null = null; /** * Messages below this level will be discarded. */ @@ -47,20 +45,6 @@ export default class Logger { }; this.store = store; this.minLevel = minLevel; - - if (config.syslog) { - this.syslogClient = new SyslogPro.RFC5424({ - applicationName: 'FoundKey', - timestamp: true, - includeStructuredData: true, - color: true, - extendedColor: true, - server: { - target: config.syslog.host, - port: config.syslog.port, - }, - }); - } } /** @@ -139,15 +123,6 @@ export default class Logger { if (envOption.withLogTime) log = chalk.gray(time) + ' ' + log; console.log(important ? chalk.bold(log) : log); - - if (store && this.syslogClient) { - const send = - level === LEVELS.error ? this.syslogClient.error : - level === LEVELS.warning ? this.syslogClient.warning : - this.syslogClient.info; - - send.bind(this.syslogClient)(message).catch(() => {}); - } } /** diff --git a/yarn.lock b/yarn.lock index 8d225b0f4..1786f0c51 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2443,13 +2443,6 @@ __metadata: languageName: node linkType: hard -"@types/syslog-pro@npm:^1.0.0": - version: 1.0.0 - resolution: "@types/syslog-pro@npm:1.0.0" - checksum: d0dcd87efad8a629bba449f86a617605a3fbffa5c18a8b309c82e7b85036ac21cfd34711fd522f50528dd0f0d07bdb66261a6f9ef20f2a9133e847b2e717c1bc - languageName: node - linkType: hard - "@types/throttle-debounce@npm:5.0.0": version: 5.0.0 resolution: "@types/throttle-debounce@npm:5.0.0" @@ -3702,7 +3695,6 @@ __metadata: "@types/sinon": ^10.0.13 "@types/sinonjs__fake-timers": 8.1.2 "@types/speakeasy": 2.0.7 - "@types/syslog-pro": ^1.0.0 "@types/tinycolor2": 1.4.3 "@types/tmp": 0.2.3 "@types/uuid": 8.3.4 @@ -3792,7 +3784,6 @@ __metadata: stringz: 2.1.0 style-loader: 3.3.1 summaly: 2.7.0 - syslog-pro: 1.0.0 systeminformation: 5.11.22 tinycolor2: 1.4.2 tmp: 0.2.1 @@ -12044,13 +12035,6 @@ __metadata: languageName: node linkType: hard -"moment@npm:^2.22.2": - version: 2.29.4 - resolution: "moment@npm:2.29.4" - checksum: 0ec3f9c2bcba38dc2451b1daed5daded747f17610b92427bebe1d08d48d8b7bdd8d9197500b072d14e326dd0ccf3e326b9e3d07c5895d3d49e39b6803b76e80e - languageName: node - linkType: hard - "ms@npm:2.0.0": version: 2.0.0 resolution: "ms@npm:2.0.0" @@ -16141,15 +16125,6 @@ __metadata: languageName: node linkType: hard -"syslog-pro@npm:1.0.0": - version: 1.0.0 - resolution: "syslog-pro@npm:1.0.0" - dependencies: - moment: ^2.22.2 - checksum: 7d6399e4ca3a9305758f77b3e720469b39c156b5a8219ed4ce27b4ad8f960f8e395aebb0ccc84e4438b50a6b2cda2e20251e278307833ed7ac1045ae9516a33c - languageName: node - linkType: hard - "systeminformation@npm:5.11.22": version: 5.11.22 resolution: "systeminformation@npm:5.11.22" From eafacbba995daef6043125f215cf47263675e099 Mon Sep 17 00:00:00 2001 From: Francis Dinh Date: Sat, 20 May 2023 06:41:39 -0400 Subject: [PATCH 29/36] docs: fix typo in migration revert script --- docs/migrating.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/migrating.md b/docs/migrating.md index 6308f667b..a44742508 100644 --- a/docs/migrating.md +++ b/docs/migrating.md @@ -20,7 +20,7 @@ cd packages/backend LINE_NUM="$(npx typeorm migration:show -d ormconfig.js | grep -n nsfwDetection1655368940105 | cut -d ':' -f 1)" NUM_MIGRATIONS="$(npx typeorm migration:show -d ormconfig.js | tail -n+"$LINE_NUM" | grep '\[X\]' | nl)" -for i in $(seq 1 $NUM_MIGRAIONS); do +for i in $(seq 1 $NUM_MIGRATIONS); do npx typeorm migration:revert -d ormconfig.js done ``` From ed9f2f49005450655b5caf9f745420fe0154e4e4 Mon Sep 17 00:00:00 2001 From: Johann150 Date: Sat, 20 May 2023 00:22:38 +0200 Subject: [PATCH 30/36] server: refactor ffVisibility checks into function --- .../backend/src/models/repositories/user.ts | 42 +++++++++++++++---- .../src/server/activitypub/followers.ts | 13 ++---- .../src/server/activitypub/following.ts | 13 ++---- .../server/api/endpoints/users/followers.ts | 23 ++-------- .../server/api/endpoints/users/following.ts | 23 ++-------- 5 files changed, 46 insertions(+), 68 deletions(-) diff --git a/packages/backend/src/models/repositories/user.ts b/packages/backend/src/models/repositories/user.ts index 10969a034..4387ed922 100644 --- a/packages/backend/src/models/repositories/user.ts +++ b/packages/backend/src/models/repositories/user.ts @@ -230,6 +230,34 @@ export const UserRepository = db.getRepository(User).extend({ return `${config.url}/identicon/${userId}`; }, + /** + * Determines whether the followers/following of user `user` are visibile to user `me`. + */ + async areFollowersVisibleTo(user: User, me: { id: User['id'] } | null | undefined): Promise { + const profile = await UserProfiles.findOneByOrFail({ userId: user.id }); + + switch (profile.ffVisibility) { + case 'public': + return true; + case 'followers': + if (me == null) { + return false; + } else if (me.id === user.id) { + return true; + } else { + return await Followings.count({ + where: { + followerId: me.id, + followeeId: user.id, + }, + take: 1, + }).then(n => n > 0); + } + case 'private': + return me?.id === user.id; + } + } + async pack( src: User['id'] | User, me?: { id: User['id'] } | null | undefined, @@ -270,15 +298,13 @@ export const UserRepository = db.getRepository(User).extend({ .getMany() : []; const profile = opts.detail ? await UserProfiles.findOneByOrFail({ userId: user.id }) : null; - const followingCount = profile == null ? null : - (profile.ffVisibility === 'public') || isMe ? user.followingCount : - (profile.ffVisibility === 'followers') && relation?.isFollowing ? user.followingCount : - null; + const ffVisible = await this.areFollowersVisibleTo(user, me); - const followersCount = profile == null ? null : - (profile.ffVisibility === 'public') || isMe ? user.followersCount : - (profile.ffVisibility === 'followers') && relation?.isFollowing ? user.followersCount : - null; + const followingCount = opts.detail ? null : + ffVisible ? user.followingCount : null; + + const followersCount = opts.detail ? null : + ffVisible ? user.followersCount : null; const packed = { id: user.id, diff --git a/packages/backend/src/server/activitypub/followers.ts b/packages/backend/src/server/activitypub/followers.ts index beb48713a..2c2b6cfb4 100644 --- a/packages/backend/src/server/activitypub/followers.ts +++ b/packages/backend/src/server/activitypub/followers.ts @@ -6,7 +6,7 @@ import { renderActivity } from '@/remote/activitypub/renderer/index.js'; import renderOrderedCollection from '@/remote/activitypub/renderer/ordered-collection.js'; import renderOrderedCollectionPage from '@/remote/activitypub/renderer/ordered-collection-page.js'; import renderFollowUser from '@/remote/activitypub/renderer/follow-user.js'; -import { Users, Followings, UserProfiles } from '@/models/index.js'; +import { Users, Followings } from '@/models/index.js'; import { Following } from '@/models/entities/following.js'; import { setResponseType } from '../activitypub.js'; @@ -31,19 +31,12 @@ export default async (ctx: Router.RouterContext) => { return; } - //#region Check ff visibility - const profile = await UserProfiles.findOneByOrFail({ userId: user.id }); - - if (profile.ffVisibility === 'private') { - ctx.status = 403; - ctx.set('Cache-Control', 'public, max-age=30'); - return; - } else if (profile.ffVisibility === 'followers') { + const ffVisible = await Users.areFollowersVisibleTo(user, null); + if (!ffVisible) { ctx.status = 403; ctx.set('Cache-Control', 'public, max-age=30'); return; } - //#endregion const limit = 10; const partOf = `${config.url}/users/${userId}/followers`; diff --git a/packages/backend/src/server/activitypub/following.ts b/packages/backend/src/server/activitypub/following.ts index 3a25a6316..4e156a19f 100644 --- a/packages/backend/src/server/activitypub/following.ts +++ b/packages/backend/src/server/activitypub/following.ts @@ -6,7 +6,7 @@ import { renderActivity } from '@/remote/activitypub/renderer/index.js'; import renderOrderedCollection from '@/remote/activitypub/renderer/ordered-collection.js'; import renderOrderedCollectionPage from '@/remote/activitypub/renderer/ordered-collection-page.js'; import renderFollowUser from '@/remote/activitypub/renderer/follow-user.js'; -import { Users, Followings, UserProfiles } from '@/models/index.js'; +import { Users, Followings } from '@/models/index.js'; import { Following } from '@/models/entities/following.js'; import { setResponseType } from '../activitypub.js'; @@ -31,19 +31,12 @@ export default async (ctx: Router.RouterContext) => { return; } - //#region Check ff visibility - const profile = await UserProfiles.findOneByOrFail({ userId: user.id }); - - if (profile.ffVisibility === 'private') { - ctx.status = 403; - ctx.set('Cache-Control', 'public, max-age=30'); - return; - } else if (profile.ffVisibility === 'followers') { + const ffVisible = await Users.areFollowersVisibleTo(user, null); + if (!ffVisible) { ctx.status = 403; ctx.set('Cache-Control', 'public, max-age=30'); return; } - //#endregion const limit = 10; const partOf = `${config.url}/users/${userId}/following`; diff --git a/packages/backend/src/server/api/endpoints/users/followers.ts b/packages/backend/src/server/api/endpoints/users/followers.ts index 2595fbff5..e93851cd8 100644 --- a/packages/backend/src/server/api/endpoints/users/followers.ts +++ b/packages/backend/src/server/api/endpoints/users/followers.ts @@ -1,5 +1,5 @@ import { IsNull } from 'typeorm'; -import { Users, Followings, UserProfiles } from '@/models/index.js'; +import { Users, Followings } from '@/models/index.js'; import { toPunyNullable } from '@/misc/convert-host.js'; import define from '@/server/api/define.js'; import { ApiError } from '@/server/api/error.js'; @@ -61,25 +61,8 @@ export default define(meta, paramDef, async (ps, me) => { if (user == null) throw new ApiError('NO_SUCH_USER'); - const profile = await UserProfiles.findOneByOrFail({ userId: user.id }); - - if (profile.ffVisibility === 'private') { - if (me == null || (me.id !== user.id)) { - throw new ApiError('ACCESS_DENIED'); - } - } else if (profile.ffVisibility === 'followers') { - if (me == null) { - throw new ApiError('ACCESS_DENIED'); - } else if (me.id !== user.id) { - const following = await Followings.countBy({ - followeeId: user.id, - followerId: me.id, - }); - if (!following) { - throw new ApiError('ACCESS_DENIED'); - } - } - } + const ffVisible = await Users.areFollowersVisibleTo(user, me); + if (!ffVisible) throw new ApiError('ACCESS_DENIED'); const query = makePaginationQuery(Followings.createQueryBuilder('following'), ps.sinceId, ps.untilId) .andWhere('following.followeeId = :userId', { userId: user.id }) diff --git a/packages/backend/src/server/api/endpoints/users/following.ts b/packages/backend/src/server/api/endpoints/users/following.ts index 0bf60c079..406853423 100644 --- a/packages/backend/src/server/api/endpoints/users/following.ts +++ b/packages/backend/src/server/api/endpoints/users/following.ts @@ -1,5 +1,5 @@ import { IsNull } from 'typeorm'; -import { Users, Followings, UserProfiles } from '@/models/index.js'; +import { Users, Followings } from '@/models/index.js'; import { toPunyNullable } from '@/misc/convert-host.js'; import define from '@/server/api/define.js'; import { ApiError } from '@/server/api/error.js'; @@ -61,25 +61,8 @@ export default define(meta, paramDef, async (ps, me) => { if (user == null) throw new ApiError('NO_SUCH_USER'); - const profile = await UserProfiles.findOneByOrFail({ userId: user.id }); - - if (profile.ffVisibility === 'private') { - if (me == null || (me.id !== user.id)) { - throw new ApiError('ACCESS_DENIED'); - } - } else if (profile.ffVisibility === 'followers') { - if (me == null) { - throw new ApiError('ACCESS_DENIED'); - } else if (me.id !== user.id) { - const following = await Followings.countBy({ - followeeId: user.id, - followerId: me.id, - }); - if (!following) { - throw new ApiError('ACCESS_DENIED'); - } - } - } + const ffVisible = await Users.areFollowersVisibleTo(user, me); + if (!ffVisible) throw new ApiError('ACCESS_DENIED'); const query = makePaginationQuery(Followings.createQueryBuilder('following'), ps.sinceId, ps.untilId) .andWhere('following.followerId = :userId', { userId: user.id }) From ded48c96d83391805c5eb8bac368b2b149d46174 Mon Sep 17 00:00:00 2001 From: Johann150 Date: Sat, 20 May 2023 00:23:18 +0200 Subject: [PATCH 31/36] server: use foundkey-js definition of ffVisibilities --- packages/backend/src/server/api/endpoints/i/update.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/backend/src/server/api/endpoints/i/update.ts b/packages/backend/src/server/api/endpoints/i/update.ts index c7f7d50c8..1a73e31df 100644 --- a/packages/backend/src/server/api/endpoints/i/update.ts +++ b/packages/backend/src/server/api/endpoints/i/update.ts @@ -1,6 +1,6 @@ import RE2 from 're2'; import * as mfm from 'mfm-js'; -import { notificationTypes } from 'foundkey-js'; +import { ffVisibility, notificationTypes } from 'foundkey-js'; import { publishMainStream, publishUserEvent } from '@/services/stream.js'; import { acceptAllFollowRequests } from '@/services/following/requests/accept-all.js'; import { publishToFollowers } from '@/services/i/update.js'; @@ -67,7 +67,7 @@ export const paramDef = { injectFeaturedNote: { type: 'boolean' }, receiveAnnouncementEmail: { type: 'boolean' }, alwaysMarkNsfw: { type: 'boolean' }, - ffVisibility: { type: 'string', enum: ['public', 'followers', 'private'] }, + ffVisibility: { type: 'string', enum: ffVisibility }, pinnedPageId: { type: 'array', items: { type: 'string', format: 'misskey:id', } }, From ef4055840b5b69472b2a6f8ab2ed478c7c976a3a Mon Sep 17 00:00:00 2001 From: Johann150 Date: Sat, 20 May 2023 00:25:41 +0200 Subject: [PATCH 32/36] BREAKING server: respect ffVisibility on stats endpoint This makes the returned values `localFollowingCount`, `remoteFollowingCount`, `followingCount`, `localFollowersCount`, `remotefollowersCount`, `followersCount` optional on the API endpoint `users/stats`. Changelog: Fixed --- .../src/server/api/endpoints/users/stats.ts | 58 +++++++++++-------- 1 file changed, 33 insertions(+), 25 deletions(-) diff --git a/packages/backend/src/server/api/endpoints/users/stats.ts b/packages/backend/src/server/api/endpoints/users/stats.ts index 970308f78..7197219fb 100644 --- a/packages/backend/src/server/api/endpoints/users/stats.ts +++ b/packages/backend/src/server/api/endpoints/users/stats.ts @@ -46,27 +46,27 @@ export const meta = { }, localFollowingCount: { type: 'integer', - optional: false, nullable: false, + optional: true, nullable: false, }, remoteFollowingCount: { type: 'integer', - optional: false, nullable: false, + optional: true, nullable: false, }, localFollowersCount: { type: 'integer', - optional: false, nullable: false, + optional: true, nullable: false, }, remoteFollowersCount: { type: 'integer', - optional: false, nullable: false, + optional: true, nullable: false, }, followingCount: { type: 'integer', - optional: false, nullable: false, + optional: true, nullable: false, }, followersCount: { type: 'integer', - optional: false, nullable: false, + optional: true, nullable: false, }, sentReactionsCount: { type: 'integer', @@ -110,7 +110,7 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps) => { +export default define(meta, paramDef, async (ps, me) => { const user = await Users.findOneBy({ id: ps.userId }); if (user == null) { throw new ApiError('NO_SUCH_USER'); @@ -141,22 +141,6 @@ export default define(meta, paramDef, async (ps) => { .innerJoin('vote.note', 'note') .where('note.userId = :userId', { userId: user.id }) .getCount(), - localFollowingCount: Followings.createQueryBuilder('following') - .where('following.followerId = :userId', { userId: user.id }) - .andWhere('following.followeeHost IS NULL') - .getCount(), - remoteFollowingCount: Followings.createQueryBuilder('following') - .where('following.followerId = :userId', { userId: user.id }) - .andWhere('following.followeeHost IS NOT NULL') - .getCount(), - localFollowersCount: Followings.createQueryBuilder('following') - .where('following.followeeId = :userId', { userId: user.id }) - .andWhere('following.followerHost IS NULL') - .getCount(), - remoteFollowersCount: Followings.createQueryBuilder('following') - .where('following.followeeId = :userId', { userId: user.id }) - .andWhere('following.followerHost IS NOT NULL') - .getCount(), sentReactionsCount: NoteReactions.createQueryBuilder('reaction') .where('reaction.userId = :userId', { userId: user.id }) .getCount(), @@ -180,8 +164,32 @@ export default define(meta, paramDef, async (ps) => { driveUsage: DriveFiles.calcDriveUsageOf(user.id), }); - result.followingCount = result.localFollowingCount + result.remoteFollowingCount; - result.followersCount = result.localFollowersCount + result.remoteFollowersCount; + const ffVisible = await Users.areFollowersVisibleTo(user, me); + if (ffVisible) { + const follows = await awaitAll({ + localFollowingCount: Followings.createQueryBuilder('following') + .where('following.followerId = :userId', { userId: user.id }) + .andWhere('following.followeeHost IS NULL') + .getCount(), + remoteFollowingCount: Followings.createQueryBuilder('following') + .where('following.followerId = :userId', { userId: user.id }) + .andWhere('following.followeeHost IS NOT NULL') + .getCount(), + localFollowersCount: Followings.createQueryBuilder('following') + .where('following.followeeId = :userId', { userId: user.id }) + .andWhere('following.followerHost IS NULL') + .getCount(), + remoteFollowersCount: Followings.createQueryBuilder('following') + .where('following.followeeId = :userId', { userId: user.id }) + .andWhere('following.followerHost IS NOT NULL') + .getCount(), + }); + + Object.assign(result, follows); + + result.followingCount = result.localFollowingCount + result.remoteFollowingCount; + result.followersCount = result.localFollowersCount + result.remoteFollowersCount; + } return result; }); From fe65cba9bec2c9cf87a51e767c22ba23ccb9e747 Mon Sep 17 00:00:00 2001 From: Johann150 Date: Sat, 20 May 2023 00:32:18 +0200 Subject: [PATCH 33/36] add "nobody" follower visibility Adds a new follower/following visibility that hides followers even from yourself. Changelog: Added --- locales/en-US.yml | 1 + .../1684536337602-ffVisibilityNobody.js | 21 +++++++++++++++++++ .../backend/src/models/repositories/user.ts | 2 ++ .../client/src/pages/settings/privacy.vue | 1 + packages/foundkey-js/src/consts.ts | 2 +- packages/foundkey-js/src/entities.ts | 2 +- 6 files changed, 27 insertions(+), 2 deletions(-) create mode 100644 packages/backend/migration/1684536337602-ffVisibilityNobody.js diff --git a/locales/en-US.yml b/locales/en-US.yml index fa0d9d28e..fe1dc137e 100644 --- a/locales/en-US.yml +++ b/locales/en-US.yml @@ -843,6 +843,7 @@ _ffVisibility: public: "Public" followers: "Visible to followers only" private: "Private" + nobody: "Nobody (not even you)" _signup: almostThere: "Almost there" emailAddressInfo: "Please enter your email address. It will not be made public." diff --git a/packages/backend/migration/1684536337602-ffVisibilityNobody.js b/packages/backend/migration/1684536337602-ffVisibilityNobody.js new file mode 100644 index 000000000..8998e7d24 --- /dev/null +++ b/packages/backend/migration/1684536337602-ffVisibilityNobody.js @@ -0,0 +1,21 @@ +export class ffVisibilityNobody1684536337602 { + name = 'ffVisibilityNobody1684536337602'; + + async up(queryRunner) { + await queryRunner.query(`ALTER TYPE "public"."user_profile_ffvisibility_enum" RENAME TO "user_profile_ffvisibility_enum_old"`); + await queryRunner.query(`CREATE TYPE "public"."user_profile_ffvisibility_enum" AS ENUM('public', 'followers', 'private', 'nobody')`); + await queryRunner.query(`ALTER TABLE "user_profile" ALTER COLUMN "ffVisibility" DROP DEFAULT`); + await queryRunner.query(`ALTER TABLE "user_profile" ALTER COLUMN "ffVisibility" TYPE "public"."user_profile_ffvisibility_enum" USING "ffVisibility"::"text"::"public"."user_profile_ffvisibility_enum"`); + await queryRunner.query(`ALTER TABLE "user_profile" ALTER COLUMN "ffVisibility" SET DEFAULT 'public'`); + await queryRunner.query(`DROP TYPE "public"."user_profile_ffvisibility_enum_old"`); + } + + async down(queryRunner) { + await queryRunner.query(`CREATE TYPE "public"."user_profile_ffvisibility_enum_old" AS ENUM('public', 'followers', 'private')`); + await queryRunner.query(`ALTER TABLE "user_profile" ALTER COLUMN "ffVisibility" DROP DEFAULT`); + await queryRunner.query(`ALTER TABLE "user_profile" ALTER COLUMN "ffVisibility" TYPE "public"."user_profile_ffvisibility_enum_old" USING "ffVisibility"::"text"::"public"."user_profile_ffvisibility_enum_old"`); + await queryRunner.query(`ALTER TABLE "user_profile" ALTER COLUMN "ffVisibility" SET DEFAULT 'public'`); + await queryRunner.query(`DROP TYPE "public"."user_profile_ffvisibility_enum"`); + await queryRunner.query(`ALTER TYPE "public"."user_profile_ffvisibility_enum_old" RENAME TO "user_profile_ffvisibility_enum"`); + } +} diff --git a/packages/backend/src/models/repositories/user.ts b/packages/backend/src/models/repositories/user.ts index 4387ed922..aea591229 100644 --- a/packages/backend/src/models/repositories/user.ts +++ b/packages/backend/src/models/repositories/user.ts @@ -255,6 +255,8 @@ export const UserRepository = db.getRepository(User).extend({ } case 'private': return me?.id === user.id; + case 'nobody': + return false; } } diff --git a/packages/client/src/pages/settings/privacy.vue b/packages/client/src/pages/settings/privacy.vue index f8b02efb6..54ee1cf8e 100644 --- a/packages/client/src/pages/settings/privacy.vue +++ b/packages/client/src/pages/settings/privacy.vue @@ -16,6 +16,7 @@ + diff --git a/packages/foundkey-js/src/consts.ts b/packages/foundkey-js/src/consts.ts index a7ea18d25..ed9fd8e53 100644 --- a/packages/foundkey-js/src/consts.ts +++ b/packages/foundkey-js/src/consts.ts @@ -4,7 +4,7 @@ export const noteNotificationTypes = ['mention', 'reply', 'renote', 'quote', 're export const mutedNoteReasons = ['word', 'manual', 'spam', 'other'] as const; -export const ffVisibility = ['public', 'followers', 'private'] as const; +export const ffVisibility = ['public', 'followers', 'private', 'nobody'] as const; export const permissions = [ 'read:account', diff --git a/packages/foundkey-js/src/entities.ts b/packages/foundkey-js/src/entities.ts index 5cec05c99..a83a8c123 100644 --- a/packages/foundkey-js/src/entities.ts +++ b/packages/foundkey-js/src/entities.ts @@ -38,7 +38,7 @@ export type UserDetailed = UserLite & { birthday: string | null; createdAt: DateString; description: string | null; - ffVisibility: 'public' | 'followers' | 'private'; + ffVisibility: 'public' | 'followers' | 'private' | 'nobody'; fields: {name: string; value: string}[]; followersCount: number; followingCount: number; From 6886ddb689a43af267f2676bc86ca5fdd8da74e6 Mon Sep 17 00:00:00 2001 From: Francis Dinh Date: Sat, 20 May 2023 00:16:46 -0400 Subject: [PATCH 34/36] docs: reformat warnings and update Misskey references in emoji docs --- docs/emoji.md | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/docs/emoji.md b/docs/emoji.md index 257cbc39d..abfd13632 100644 --- a/docs/emoji.md +++ b/docs/emoji.md @@ -18,12 +18,11 @@ Please note that Emoji may be subject to copyright and you are responsible for c If you have an image file that you would like to turn into a custom emoji you can import the image as an emoji. This works just like attaching files to a note: -You can choose to upload a new file, pick a file from your Misskey drive or upload a file from another URL. +You can choose to upload a new file, pick a file from your Foundkey drive or upload a file from another URL. -::: danger +**Warning:** When you import emoji from your drive, the file will remain inside your drive. -Misskey does not make a copy of this file so if you delete it, the emoji will be broken. -::: +Foundkey does not make a copy of this file so if you delete it, the emoji will be broken. The emoji will be added to the instance and you will then be able to edit or delete it as usual. @@ -32,10 +31,9 @@ The emoji will be added to the instance and you will then be able to edit or del Emojis can be imported in bulk as packed ZIP files with a special format. This ability can be found in the three dots menu in the top right corner of the custom emoji menu. -::: warning +**Warning:** Bulk emoji import may overwrite existing emoji or otherwise mess up your instance. Be sure to only import emoji from trusted sources, ideally only ones you exported yourself. -::: ### Packed emoji format @@ -89,10 +87,9 @@ The properties of an emoji can be edited by clicking it in the list of local emo When you click on a custom emoji, a dialog for editing the properties will open. This dialog will also allow you to delete an emoji. -::: danger +**Warning:** When you delete a custom emoji, old notes that contain it will still have the text name of the emoji in it. The emoji will no longer be rendered correctly. -::: Note that remote emoji can not be edited or deleted. From 38193cd3e5cdc73a1dcef808887393f22d46115b Mon Sep 17 00:00:00 2001 From: Johann150 Date: Sat, 20 May 2023 23:38:23 +0200 Subject: [PATCH 35/36] fixup: typo in ffVisibility setting This is a fixup for commit fe65cba9bec2c9cf87a51e767c22ba23ccb9e747. --- packages/client/src/pages/settings/privacy.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/client/src/pages/settings/privacy.vue b/packages/client/src/pages/settings/privacy.vue index 54ee1cf8e..7c99bf83a 100644 --- a/packages/client/src/pages/settings/privacy.vue +++ b/packages/client/src/pages/settings/privacy.vue @@ -16,7 +16,7 @@ - + From 7a94e9f2d5804b6331272cc5d9db2ff12d8fde05 Mon Sep 17 00:00:00 2001 From: Johann150 Date: Sun, 21 May 2023 00:13:09 +0200 Subject: [PATCH 36/36] remove overstriking from korean translation --- locales/ko-KR.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/locales/ko-KR.yml b/locales/ko-KR.yml index 69a2ad1af..a729d0a3a 100644 --- a/locales/ko-KR.yml +++ b/locales/ko-KR.yml @@ -842,8 +842,8 @@ _ffVisibility: private: "비공개" _signup: almostThere: "거의 다 끝났습니다" - emailAddressInfo: "당신이 사용하고 있는 이메일 주소를 입력해 주세요. 이메일 주소는 다른 유저에게 공개되지 않습니다." - emailSent: "입력하신 메일 주소({email})로 확인 메일을 보내드렸습니다. 가입을 완료하시려면 보내드린 메일에 있는 링크로 접속해 주세요." + emailAddressInfo: "당신이 사용하고 있는 이메일 주소를 입력해 주세요. 이메일 주소는 다른 유저에게 공개되지 않습니다." + emailSent: "입력하신 메일 주소({email})로 확인 메일을 보내드렸습니다. 가입을 완료하시려면 보내드린 메일에 있는 링크로 접속해 주세요." _accountDelete: accountDelete: "계정 삭제" mayTakeTime: "계정 삭제는 서버에 부하를 가하기 때문에, 작성한 콘텐츠나 업로드한 파일의 수가 많으면 완료까지 시간이 걸릴 수 있습니다."